Qodana 2026.2 Help

Configuration reference

Introduction

You can configure Qodana using YAML or command-line (CLI) options.

Configuring Qodana via a YAML-formatted file typically named qodana.yaml and contained in the root directory of your project is suitable for settings that require lengthy commands. For example, inspection configuration, bootstrap, and other settings that are not convenient to configure otherwise. Once a YAML configuration is saved, you can reuse it across different instances of Qodana.

CLI options are suitable for immediate configuration of applications that run Qodana like the Docker engine, Qodana CLI, IDEs, and CI/CD tools. Besides that, some CLI options do not have YAML equivalents.

You can configure linter and quality gates via both YAML and CLI. In such cases, CLI options take precedence over their YAML equivalents if both methods are used.

YAML configuration

By default, this configuration capability will be referred to as the qodana.yaml configuration. To start, in the root directory of your project save the qodana.yaml file.

To override the qodana.yaml filename and its location, follow the recommendations of the Override YAML configuration file chapter.

The JSON schema for qodana.yaml is published in the SchemaStore project, which provides completion and basic validation in IDEs.

CLI options

You can configure Qodana using three types of CLI options as shown below.

Option type

Example

Requires the equal sign (=) between the option name and its argument

--property=idea.log.config.file=info.xml

Requires the space character ( ) between the option name and its argument

--baseline /path/to/sarif/file

Requires no argument

--apply-fixes

Here are the examples that invoke all these option types:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ <image-name> \ --property=idea.log.config.file=info.xml \ --baseline <baseline-path> \ --apply-fixes
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --property=idea.log.config.file=info.xml \ --baseline <baseline-path> \ --apply-fixes
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: | --property=idea.log.config.file=info.xml --baseline <baseline-path> --apply-fixes env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --property=idea.log.config.file=info.xml \ --baseline <baseline-path> \ --apply-fixes ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --property=idea.log.config.file=info.xml --baseline <baseline-path> --apply-fixes --image <image>

In the runner configuration, find the Additional Qodana arguments field and specify the path to the plugin file:

--property=idea.log.config.file=info.xml --baseline <baseline-path> --apply-fixes

Run custom commands

During analyses, Qodana linters may report that some inspections cannot find classes, packages, files or cannot resolve references, although linters related to JVM, .NET, and Golang try to figure out the build system and project structure automatically.

When you run Qodana on a CI/CD platform, project dependencies are usually not installed automatically, so you may need to install the dependencies yourself. In these cases, Qodana needs a bit of help:

  • Install third-party packages or libraries

  • Run a program that sets up the build environment

These actions are carried out using the bootstrap key:

version: "1.0" linter: <linter> bootstrap: |+ set -eu # For PHP projects that use Laravel: #composer require --dev barryvdh/laravel-ide-helper # For JavaScript projects that use Node.js: #npm install # For Python projects #pip install -r requirements.txt

To be able to use syntax highlighting and validation in your IDE, you can create the prepare-qodana.sh shell script and save it in the root directory of your project:

#! /bin/sh set -eu # For PHP projects that use Laravel: #composer require --dev barryvdh/laravel-ide-helper # For JavaScript projects that use Node.js: #npm install

Run the script in a Qodana Docker container using the bootstrap key:

bootstrap: sh ./prepare-qodana.sh

To run Qodana as the root user, you may need to invoke the --user=root option.

In CI/CD environments like GitLab CI/CD, you may need to prepare a complex environment before the analysis starts. Here is an example of a monorepo setup with a frontend directory (Node.js) and a backend directory (C#):

version: "1.0" linter: qodana-dotnet bootstrap: |+ set -eu # Install frontend dependencies echo "Installing frontend dependencies..." cd frontend && npm install && cd .. # Build backend C# projects echo "Building C# projects..." dotnet restore backend/MySolution.sln dotnet build backend/MySolution.sln --no-restore

You can also see the Monorepo projects section.

Specify a linter

Using the linter YAML key, you can specify a linter that you are going to employ. For example:

version: "1.0" linter: qodana-jvm-android

Alternatively, you can use the --linter CLI option as shown below:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="&lt;cloud-project-token&gt;" \ jetbrains/qodana-&lt;image&gt; \ --linter <linter>
qodana scan \ -e QODANA_TOKEN="&lt;cloud-project-token&gt;" \ --linter <linter>
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --linter <linter> env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-&lt;image&gt;' } } stages { stage('Qodana') { steps { sh ''' qodana \ --linter <linter> ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --linter <linter>

In the runner configuration, find the Additional Qodana arguments field and specify the script:

--linter <linter>

Configure native mode

The native mode lets you run Qodana without the Docker engine and is available for the following linters:

Linter name

Description

qodana-jvm

Qodana for JVM

qodana-jvm-community

Qodana Community for JVM

qodana-php

Qodana for PHP

qodana-js

Qodana for JS

qodana-dotnet

Qodana for .NET

qodana-go

Qodana for Go

qodana-python

Qodana for Python

qodana-python-community

Qodana Community for Python

You can configure native mode by specifying a linter, as well as set the withinDocker setting to false.

In case of a YAML configuration, this may look as follows:

version: "1.0" linter: <linter-name> withinDocker: false # Setting Docker mode to false

The --linter and --within-docker false CLI options also let you configure the native mode:

qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --linter <linter> \ --within-docker false
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: | --linter <linter> --within-docker false env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}

Override YAML configuration file

Your project can have several Qodana configurations contained in YAML-formatted files. This comes in handy if you analyze monorepo projects or run a single CI job.

You can use the --config CLI option and a path to a file relative to the project root:

docker run \    -v $(pwd):/data/project \    -e QODANA_TOKEN="<cloud-project-token>" \    jetbrains/qodana-<image> \    --config relative/path/to/config.yaml
qodana scan \    -e QODANA_TOKEN="<cloud-project-token>" \    --config relative/path/to/config.yaml
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --config relative/path/to/config.yaml env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --config relative/path/to/config.yaml ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --config relative/path/to/config.yaml --linter <linter>

In the runner configuration, find the Additional Qodana arguments field and specify the path to your custom configuration file:

--config relative/path/to/config.yaml

Inspection profile

By default, Qodana analyzes your code using the qodana.starter profile. You can switch to another existing profile using the recommendations from the Existing Qodana profiles chapter. To set up your own profile, use available configuration options described in the Custom profiles chapter.

YAML configuration

Using the profile YAML key, you can import an existing configuration from a dedicated file, as well as customize the configuration once it is imported:

Use the profile.path key to invoke an existing profile by its name, for example:

version: "1.0" linter: <linter> profile: name: qodana.recommended inspections: # Configuring the invoked profile — group: "category:Java/Probable bugs" enabled: true # Enabling the inspection category — inspection: RedundantIf enabled: false # Disabling the inspection

After you set up a profile in a dedicated file as described in the Inspection profiles section, you can invoke it in the qodana.yaml file using the profile.path key, for example:

version: "1.0" linter: <linter> profile: path: .qodana/profiles/<custom-profile.yaml> inspections: # Configuring the invoked profile — group: "category:Java/Probable bugs" enabled: true # Enabling the inspection category — inspection: RedundantIf enabled: false # Disabling the inspection

CLI options

You can use the following profile-related CLI options:

Option

Description

Default setting

--disable-sanity

Skip running the inspections configured by the qodana.sanity profile

Enabled

-n, --profile-name

The profile name from the list of existing Qodana profiles.

Alternatively, you can use a profile name from a custom profile file. To do it, you will need to mount a profile file to Qodana.

qodana.starter

-p, --profile-path

The absolute path to the profile file.

None

--run-promo

Run promo inspections as a part of the qodana.starter profile

Enabled only if Qodana is configured for the qodana.starter profile, and the --run-promo true option is invoked

Below are the configuration snippets containing the -profile-name option for invoking a profile by its name, as well as the --profile-path CLI option for invoking a profile configuration from a dedicated file:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ -v <path-to-profile-file> \ # Mount the file containing custom profile --profile-name qodana.recommended | <profile-name-from-file> \ # Existing profile name | Name from mounted file --profile-path /data/project/myprofiles/<file-name> # Importing profile file
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ -v <path-to-profile-file> \ # Mount the file containing custom profile --profile-name qodana.recommended | <profile-name-from-file> \ # Existing profile name | Name from mounted file --profile-path /data/project/myprofiles/<file-name> # Importing profile file
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: | -v <path-to-profile-file> # Mount the file containing custom profile --profile-name qodana.recommended | <profile-name-from-file> # Existing profile name | Name from mounted file --profile-path /data/project/myprofiles/<file-name> # Importing profile file env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project -v <path-to-profile-file> // Mount the file containing custom profile --entrypoint="" ''' image 'jetbrains/qodana-<image> } } stages { stage('Qodana') { steps { sh ''' qodana \ --profile-name qodana.recommended | <profile-name-from-file> // Existing profile name | Name from mounted file --profile-path /data/project/myprofiles/<file-name> // Importing profile file ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --linter <linter> -v <path-to-profile-file> \ # Mount the file containing custom profile --profile-name qodana.recommended | <profile-name-from-file> \ # Existing profile name | Name from mounted file --profile-path /data/project/myprofiles/<file-name> # Importing profile file

Configure the runner as described in the Basic configuration chapter.

Disable sanity inspections

Sanity problems refer to problems in the project configuration. By default, sanity inspections are enabled in Qodana. You can disable them using this snippet:

version: "1.0" linter: <linter> disableSanityInspections: true

Alternatively, you can use the --disable-sanity CLI option:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ <image-name> \ --disable-sanity
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --disable-sanity
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: | --disable-sanity env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --disable-sanity ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --disable-sanity --image <image>

In the runner configuration, find the Additional Qodana arguments field and specify the --disable-sanity option.

Inspections

Including inspections

You can tell Qodana to analyze files of a certain directory using an inspection that is not contained in the selected profile. This can be done on a per-analysis basis. To include all paths in a project into the inspection scope, omit the paths node. Information about inspection IDs is available on the Inspectopedia website.

In this YAML configuration example, the empty profile, which contains no inspections, is specified, and the SomeInspectionId inspection is explicitly included in the analysis scope for the tools directory. As a result, only the analysis performed by the SomeInspectionId inspection the tools directory contents will be included in the Qodana analysis scope.

version: "1.0" linter: <linter> profile: name: empty inspections: - inspection: SomeInspectionId ignore: - "!tools" enabled: true

Here, each inspections entry should reference an inspection registered in an active inspection profile, either enabled or disabled. If an inspection ID is not registered, it becomes silently ignored and no inspection is added.

For example, the profile.name: empty configuration implies that plugin-provided inspections like CppClangTidy* are not registered. To enable specific plugin inspections, you can start from an existing inspection profile like qodana.starterand suppress the inspections that you do not need. Also, add inspections using plugin-specific config like .clang-tidy in case of the C / C++ linter.

Excluding inspections

To disable inspections for a specific file, use the following YAML configuration:

version: "1.0" linter: <linter> profile: inspections: - inspection: SomeInspectionId ignore: - "!<path/to/the/file/from/project/root>" enabled: false

You can also suppress the inspection only for a class by adding the noinspection comment above the class:

// noinspection <inspection-name> export class WorkflowJobSubject { private static subject: Observable<GithubEvent<WorkflowJobEvent>> | null = null; private static GithubWebhookEventSubject: any;

Linter paths

This table lists the paths available in Qodana linters.

Path

Description

/data/project

Root directory of the project

/data/results

Directory to store analysis reports. It should be empty before running Qodana

/opt/idea

IDE distributive directory

/root/.config/idea

IDE configuration directory

/data/profile.xml

The default profile file containing the qodana.starter profile configuration. This file is used if a profile was not previously configured either via the CLI or the qodana.yaml file. See Order of resolving a profile for details

/data/project/.idea/inspectionProfiles/

Directory for binding profile files

/data/cache/.m2

Maven project dependencies

/root/.m2/

Directory for overriding the settings.xml configuration file for Maven

/data/cache/gradle

Gradle project dependencies

/data/cache/nuget

NuGet project dependencies

/data/coverage

Directory for mapping code coverage files

You can find below several examples of how these paths can be applied.

Configure Maven

In Maven, you can configure the source and target versions of the Java compiler. Qodana compares these values and selects the latest version. This version of the JDK is then searched in the list of available versions. If found, Qodana will download and use it. Otherwise, Qodana will download the subsequent version from this list.

You can specify the path to a custom Maven settings file, which lets you use custom repositories, mirrors, credentials, and local repository settings:

version: "1.0" linter: <linter> mavenSettingsPath: /path/to/settings.xml
qodana scan \ -e QODANA_TOKEN="&lt;cloud-project-token&gt;" \ --maven-settings-path /path/to/settings.xml

This setting lets Qodana parse the path, resolve relative paths against the project root, expose it in the YAML schema, and apply the configured settings.xml file prior to Maven project import or reimport. It also ensures the correct workflow order: Maven reimport (if requested) runs only after Maven settings are configured, which then updates module dependencies.

Override Gradle settings

For JVM linters, you can override the default Gradle settings:

docker run \ -v $(pwd):/data/project/ \ -v $(pwd)/gradle.properties:/data/cache/gradle/gradle.properties \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image>
qodana scan \ -v $(pwd)/gradle.properties:/data/cache/gradle/gradle.properties \ -e QODANA_TOKEN="<cloud-project-token>"
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: -v gradle.properties:/data/cache/gradle/gradle.properties env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project -v "${WORKSPACE}"/gradle.properties:/data/cache/gradle/gradle.properties --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh '''qodana''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | -v gradle.properties:/data/cache/gradle/gradle.properties --linter <linter>

In the runner configuration, find the Additional Docker arguments field and specify the path to the file containing new Gradle settings:

-v gradle.properties:/data/cache/gradle/gradle.properties

Mount JDK

You can mount JDK from your local filesystem to the /root/.jdks directory of the Qodana Docker image:

$ docker run \ -v /path/to/jdk:/root/.jdks/jdk \ jetbrains/qodana-<linter>

View Qodana logs

Depending on the tool, you can view log files generated by Qodana:

You can mount the $(pwd)/.qodana/results/ directory to the /data/results directory of the Docker image:

docker run \ -v $(pwd):/data/project/ \ -v $(pwd)/.qodana/results/:/data/results \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image>

Once the Qodana run is complete, you can view log files in the $(pwd)/.qodana/results/ directory.

After running Qodana, in the project root run the $ qodana show -d command for opening the directory containing log files.

There are several options for examining Qodana behavior using the /data/results directory:

  • The /data/results/projectStructure directory.

    The Modules.json file in this directory contains a list of all modules detected by Qodana. It should be identical to the list that you expect to see while opening your project in IntelliJ IDEA. If this is no longer the case, check pom.xml for Maven or the build.gradle file for Gradle configurations.

    The SDKs.json file in this directory contains the interpreter paths in case of Python.

  • In the /data/results/ directory, each inspection that detected a possible problem creates its own file named ID.json, where ID is the inspection name that can be used in qodana.yaml for including or excluding inspections. You can find the complete list of inspection IDs in the /data/results/.descriptions.json file using the /groups/*/inspections/*/shortName pattern.

  • In /data/results/log/idea.log, you can investigate suspicious warnings.

Analysis directories

Using these CLI options, you can override the paths described in the Linter paths section.

Option

Description

Default setting

--repository-root <string>

Specify the VCS root directory for your project. This option is required for Git-related operations

None

-i, --project-dir

Root directory of the inspected project can be either a subdirectory of --repository-root or identical to it.

Files and directories contained in the outside directory are not used while running Qodana

/data/project

-o, --results-dir

Directory to save Qodana inspection results to

/data/results

-r, --report-dir

Directory for saving the generated HTML report. To open the report, you will need to add the --save-report option

/data/results/report

--cache-dir

Directory to store cache

/data/cache

-d, --source-directory

Directory inside --project-dir. If missing, the whole project is inspected.

Files and directories contained in the outside directory like .git and build.gradle are used by Qodana while inspecting code

None

--only-directory <string>

Specify the directory inside the project-dir directory that must be analyzed. If not specified, the whole project will be analyzed

Files and directories contained in the outside directory like .git and build.gradle are used by Qodana while inspecting code

None

Override the report directory

This Docker command overrides the default report directory using the --report-dir option, and saves the generated report to the local filesystem using the --save-report option:

docker run \ -v $(pwd):/data/project/ \ -v <html-report-directory>:/data/results/newreportdir/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --report-dir /data/results/newreportdir/ \ --save-report

The generated report is saved to the local filesystem as configured by the -v <html-report-directory>:/data/results/newreportdir/ line in this command.

Analyze a specific project directory within a repository

A typical project structure can have a directory structure similar to this:

repo/ .git/ project/ ...

Here, the repo/.git directory contains information that should be accessible to Qodana, and the repo/project directory contains the project that needs to be inspected by Qodana. All these samples mount the repo/project directory using the --project-dir option, while the QODANA_TOKEN variable refers to the Qodana Cloud project token:

docker run \ -v repo/:/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --project-dir=/data/project/project/
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --project-dir=/data/project/project/
name: Qodana on: workflow_dispatch: pull_request: push: branches: - main - 'releases/*' jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --project-dir project env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}

Exclude paths from the analysis scope

You can exclude files and paths from analyses on a per-analysis basis and for all inspections at once. Information about inspection IDs is available on the Inspectopedia website.

To exclude all paths in a project from the analysis scope, omit the paths node.

Exclude all inspections for specified project paths using the following YAML configuration:

version: "1.0" linter: <linter> profile: inspections: - group: ALL ignore: - "asm-test/src/main/java/org" - "asm/Visitor.java" - "benchmarks" - "vendor/**" - "scope#file[*test*]:src/*"

Exclude inspections specified by ID for specified project paths:

version: "1.0" linter: <linter> profile: inspections: - inspection: InspectionId ignore: - "relative/path" - "another/relative/path" - group: ALL ignore: - "asm-test/src/main/java/org" - "asm" - "benchmarks" - "tools"

You can find specific inspection IDs in the Profile settings in the HTML report or in the .xml file with your inspection profile.

Specify directory in your project

Use the onlyDirectory YAML option to specify a project directory to analyze. This should be relative to the project root, for example:

version: "1.0" linter: <linter> onlyDirectory: project-a

Alternatively, you can use the --only-directory CLI option, for example:

docker run \ -v repo/:/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --only-directory project-a
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --only-directory project-a
name: Qodana on: workflow_dispatch: pull_request: push: branches: - main - 'releases/*' jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --only-directory project-a env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}

This is useful while analyzing monorepo projects.

Cache dependencies

You can improve Qodana performance by persisting cache between analyses. For example, package and dependency management tools such as Maven, Gradle, npm, Yarn, and NuGet keep a local cache of downloaded dependencies.

By default, Qodana save caches to the /data/cache directory inside a container. You can override this location using the --cache-dir option. This data is per-repository, so you can pass cache from branch-a to build checking branch-b. In this case, only new dependencies would be downloaded if they were added.

In a GitHub workflow, you can use dependency caching. GitLab CI/CD also has the cache that can be stored only inside the project directory. In this case, you can exclude the cache directory from inspection via qodana.yaml.

This command maps the local directory with the /data/cache directory of the Docker image, which saves cache to your local filesystem:

docker run \ -v $(pwd):/data/project/ \ -v <local-cache-directory>:/data/cache/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image>

Using the --cache-dir option, you can override the cache directory:

docker run \ -v $(pwd):/data/project/ \ -v <local-cache-directory>:/data/newcachedir/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --cache-dir /data/newcachedir
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --cache-dir /opt/newcachedir
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --cache-dir /data/newcachedir env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --cache-dir /data/newcachedir ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --cache-dir /data/newcachedir --linter <linter>

In the runner configuration, find the Additional Qodana arguments field and specify the path to the cache directory:

--cache-dir /data/newcachedir

Analysis reports

Qodana provides the following CLI options for managing reports:

Option

Description

-s, --save-report

Generate and save HTML-formatted reports

-w, --show-report

Serve HTML-formatted reports. By default, Qodana uses port 8080 on its side

The --save-report option in the Docker command lets you save the generated HTML report to your local filesystem:

docker run \ -v $(pwd):/data/project/ \ -v <directory-to-save-report-to>:/data/results/report \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --save-report

The --show-report option runs a local web server on port 4040 of a host machine, so your report will be available on http://localhost:4040:

docker run \ -p 4040:8080 \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --show-report
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --port 4040 \ --show-report

Alternatively, in the project root you can run the qodana show command.

To stop the web server, press Ctrl-C in the Docker console.

Qodana features

Quality gates

You have several options to configure quality gates.

In your YAML configuration, you can add a fail threshold to control the total number of problems in a project, which is supported by all linters:

version: "1.0" linter: <linter> failThreshold: <number>

All linters except Qodana Community for .NET and Qodana Community for C/C++ let you use the following configuration:

version: "1.0" linter: <linter> failureConditions: severityThresholds: any: <number> # Total problems critical: <number> # Critical and other severities high: <number> moderate: <number> low: <number> info: <number> testCoverageThresholds: fresh: <number> # Fresh code coverage total: <number> # Total code coverage dependencyLicenses: failOnProhibited: <true|false> # Prohibited licenses failOnUnknown: <true|false> # Unknown licenses

In this configuration, exceeding just one setting limitation will make the build fail.

The severityThresholds:any key lets you configure the total number of problems. The severityThresholds:critical key lets you configure quality gates for each problem severity. The testCoverageThresholds:fresh and testCoverageThresholds:total keys let you configure the total and fresh code coverage supported by several linters. The dependencyLicenses key lets you configure quality gates for prohibited or unknown licenses.

Also, you can configure quality gates for the total number of problems using the --fail-threshold CLI option. Here is the command that tells Qodana to fail the build in case the number of problems exceeds 10:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --fail-threshold 10
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --fail-threshold 10
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --fail-threshold 10 env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --fail-threshold 10 ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --fail-threshold 10 --linter <linter>

In the runner configuration, find the Additional Qodana arguments field and specify a quality gate:

--fail-threshold 10

If you run Qodana with the baseline mode enabled, a threshold is calculated as the sum of new and absent problems. The unchanged results are ignored.

Baseline

In the baseline run mode, each new Qodana run is compared to some initial run. This can help in situations when you have no possibility to fix old problems and rather want to prevent the appearance of new ones.

To use the baseline feature, first run Qodana, and in the report UI select the problems that will be considered as baseline. Finally, save the SARIF-formatted file containing the baseline problems.

This is the list of baseline-related options:

Option

Description

-b, --baseline

Run Qodana in the baseline mode. Provide the path to an existing SARIF report to be used in the baseline state calculation

--baseline-include-absent

Include in the output report the results from the baseline run that are absent during the current analysis

This command invokes all baseline options:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --baseline <path-to-the-SARIF-file> \ --baseline-include-absent
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --baseline <path-to-the-SARIF-file> \ --baseline-include-absent
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: | --baseline <path-to-the-SARIF-file> --baseline-include-absent env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --baseline <path-to-the-SARIF-file> \ --baseline-include-absent ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --baseline <path-to-the-SARIF-file> --baseline-include-absent --linter <linter>

In the runner configuration, find the Additional Qodana arguments field and configure a baseline:

--baseline <path-to-the-SARIF-file> --baseline-include-absent --linter <linter>

Here, the <path-to-the-SARIF-file> is the path to a qodana.sarif.json file relative to the project root and taken from a previous Qodana run. If --baseline-include-absent is invoked, the inspection results will include absent problems or the problems detected only in the baseline run but not in the current run.

Based on this run, the SARIF output report will contain the per-problem information on the baseline state.

License audit

The license audit feature is enabled by default. You can disable it by excluding the CheckDependencyLicenses inspection:

version: "1.0" linter: <linter> profile: inspections: - inspection: CheckDependencyLicenses enabled: false

Ignore a dependency

Ignore a dependency to hide the related problems from the report:

version: "1.0" linter: <linter> dependencyIgnores: - name: "enry"

where name is the dependency name to ignore.

In the example above, the enry dependency is completely excluded from the analysis. Because any possible license-related problems are dismissed, the dependency won't be included in the report at all. This is useful to quickly hide internal dependencies that do not need to be mentioned in the report.

Allow or prohibit a license

Override the predefined license compatibility matrix:

version: "1.0" linter: <linter> licenseRules: - keys: - "PROPRIETARY-LICENSE" - "MIT" prohibited: - "BSD-3-CLAUSE-NO-CHANGE" allowed: - "ISC" - keys: [ "Apache-2.0" ] prohibited: - "MIT"

In this configuration, keys is the project license(s) and the dependency licenses identifiers are specified in allowed or prohibited.

Override a dependency license

Override a dependency license identifier:

version: "1.0" linter: <linter> dependencyOverrides: - name: "jaxb-runtime" version: "2.3.1" url: "https://github.com/javaee/jaxb-v2" licenses: - key: "CDDL-1.1" url: "https://github.com/javaee/jaxb-v2/blob/master/LICENSE" - key: "GPL-2.0-with-classpath-exception" url: "https://github.com/javaee/jaxb-v2/blob/master/LICENSE"

where name is the dependency name, version is the dependency version, and licenses is the list of redefined dependency licenses.

In the example above, you 'tell' Qodana to detect CDDL-1.1, GPL-2.0-with-classpath-exception, and no other licenses for jaxb-runtime (only 2.3.1). This is useful when a dependency is dual-licensed, and you want to omit some license or when it's not possible to detect the license from the dependency sources correctly.

Custom dependencies

Currently, the license audit with Qodana is possible only for JPS, Maven, Gradle, npm, yarn, and composer projects. To include the dependency that should be mentioned in the report but is impossible to detect from the project sources, use customDependencies to specify it:

version: "1.0" linter: <linter> customDependencies: - name: ".babelrc JSON Schema (.babelrc-schema.json)" version: "JSON schema for Babel 6+ configuration files" licenses: - key: "Apache-2.0" url: "https://github.com/SchemaStore/schemastore/blob/master/LICENSE"

Quick-Fixes

This table describes the available Quick-Fix strategies:

CLI option / YAML key

Description

--apply-fixes / apply

Apply all available Quick-Fix strategies including cleanup, see the Quick-Fix section for details

--cleanup / cleanup

Run the CLEANUP Quick-Fix strategy, see the Quick-Fix for details

Using the fixesStrategy YAML key, you can choose among the available strategies:

version: "1.0" linter: <linter> fixesStrategy: cleanup/apply

Alternatively, you can employ the --apply-fixes or --cleanup command-line options:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --apply-fixes/cleanup
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --apply-fixes/cleanup
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --apply-fixes/cleanup env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --apply-fixes/cleanup ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --apply-fixes/cleanup --linter <linter>

In the runner configuration, find the Additional Qodana arguments field and specify the option:

--apply-fixes

Taint analysis

In the qodana.yaml file, include the PhpVulnerablePathsInspection inspection into the analysis scope:

version: "1.0" linter: qodana-php profile: inspections: — inspection: PhpVulnerablePathsInspection enabled: true

In the qodana.yaml file, include the JvmTaintAnalysis inspection into the analysis scope:

version: "1.0" linter: qodana-jvm profile: inspections: — inspection: JvmTaintAnalysis enabled: true

Vulnerability checker

To start using the Vulnerability checker feature, enable the VulnerableLibrariesGlobal inspection:

version: "1.0" linter: <linter> profile: inspections: - inspection: VulnerableLibrariesLocal enabled: true

Code coverage

By default, code coverage is enabled in Qodana.

Using the coverage.reportProblems key, you can configure it in a YAML file. The codeCoverageLocations key lets you override the default code coverage directories, for example:

version: "1.0" linter: <linter> coverage: reportProblems: true # Code coverage is enabled codeCoverageLocations: # Directories containing coverage reports - "custom-directory-1" - "custom-directory-2"

You can run the code coverage by mapping the directory containing code coverage files to the /data/coverage directory of a Qodana linter image:

docker run \ -v /my/dir/with/coverage:/data/coverage \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image>
qodana scan \ -v /my/dir/with/coverage:/data/coverage \ -e QODANA_TOKEN="<cloud-project-token>"
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: -v /my/dir/with/coverage:/data/coverage env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ -v /my/dir/with/coverage:/data/coverage ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | -v /my/dir/with/coverage:/data/coverage --linter <linter>

In the runner configuration, find the Additional Docker arguments field and specify the path the file containing code coverage results:

-v /my/dir/with/coverage:/data/coverage

Override the run scenario

You can use the following run scenarios:

Scenario name

Description

default

The default Qodana scenario, enabled by default

php-migration

PHP version migration scenario

local-changes

Analyze only uncommitted changes. You can see the Incremental analysis section to learn more about incremental analysis

Using the script of a YAML configuration, you can specify a run scenario:

version: "1.0" linter: <linter> script: name: <script-name> parameters: <parameter>: <value>

By default, Qodana employs the default scenario, which means the normal Qodana run equivalent to this setting:

version: "1.0" linter: <linter> script: name: default

You can also use the --script CLI option to configure a run scenario:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --script default
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --script default
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches   jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --script default env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --script default ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --script default --linter <linter>

In the runner configuration, find the Additional Qodana arguments field and specify the script:

--script default

JDK version

You can configure the JDK version for these linters:

Use the projectJDK key to specify the JDK version, for example:

version: "1.0" linter: <linter> projectJDK: 11

or:

version: "1.0" linter: <linter> projectJDK: 1.8

You can also specify the JDK by name:

version: "1.0" linter: <linter> projectJDK: corretto-19

To learn more about configuring JDK, see the Configuring the JDK section.

PHP version

You can configure the PHP version before running the Qodana for PHP linter:

version: "1.0" linter: qodana-php php: version: "X.x"

Properties

Using the --property= option, you can override various Qodana parameters:

Option

Description

--property=

Set a JVM property using this notation:

--property=property.name=value1,...,valueN

This option can be repeated multiple times for setting multiple JVM properties.

Log INFO messages to STDOUT

The default log level for STDOUT is WARN. You can override it using the idea.log.config.file property.

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --property=idea.log.config.file=info.xml
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --property=idea.log.config.file=info.xml
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --property=idea.log.config.file=info.xml env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --property=idea.log.config.file=info.xml ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --property=idea.log.config.file=info.xml --linter <linter>

In the runner configuration, find the Additional Qodana arguments field and specify the preferred Quick-Fix strategy:

--property=idea.log.config.file=info.xml

Disable user statistics

To disable reporting of usage statistics, adjust the idea.headless.enable.statistics value of the --property option:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --property=idea.headless.enable.statistics=false
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --property=idea.headless.enable.statistics=false
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --property idea.headless.enable.statistics=false env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --property=idea.headless.enable.statistics=false ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --property idea.headless.enable.statistics=false --image <image>

In the runner configuration, find the Additional Qodana arguments field and specify the required property:

--property=idea.headless.enable.statistics=false

Configure plugins

Using the idea.required.plugins.id and idea.suppressed.plugins.id properties, you can specify the plugins required for a specific run, and the list of plugins that will be suppressed:

docker run \ -v $(pwd):/data/project/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image> \ --property=idea.required.plugins.id=JavaScript,org.intellij.grails \ --property=idea.suppressed.plugins.id=com.intellij.spring.security
qodana scan \ -e QODANA_TOKEN="<cloud-project-token>" \ --property=idea.required.plugins.id=JavaScript,org.intellij.grails \ --property=idea.suppressed.plugins.id=com.intellij.spring.security
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: | --property=idea.required.plugins.id=JavaScript,org.intellij.grails --property=idea.suppressed.plugins.id=com.intellij.spring.security env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --property=idea.required.plugins.id=JavaScript,org.intellij.grails \ --property=idea.suppressed.plugins.id=com.intellij.spring.security ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --property=idea.required.plugins.id=JavaScript,org.intellij.grails --property=idea.suppressed.plugins.id=com.intellij.spring.security --image <image>

In the runner configuration, find the Additional Qodana arguments field and specify the required properties:

--property=idea.required.plugins.id=JavaScript,org.intellij.grails \ --property=idea.suppressed.plugins.id=com.intellij.spring.security

Setting up configuration timeout

Using the following properties, you can configure the configuration stage timeout:

Property

Available for the linters

qd.cpp.startup.timeout.minutes

Qodana for C/C++ and Qodana Community for C/C++

qd.rust.configuration.timeout.minutes

Qodana for Rust

Here are the examples of property usage:

name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --property qd.<cpp|rust>.startup.timeout.minutes=10 env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
qodana scan \    -e QODANA_TOKEN="<cloud-project-token>" \    --linter <linter> \    --property qd.<cpp|rust>.configuration.timeout.minutes=10
docker run \    -v $(pwd):/data/project/ \    -e QODANA_TOKEN="<cloud-project-token>" \    jetbrains/qodana-<image> \    --property qd.<cpp|rust>.configuration.timeout.minutes=10

Analyze changed code

Option

Description

--diff-start and --diff-end

Run incremental analysis on a change set like merge or pull requests

If you just finished work and would like to analyze the changes, you can employ the --diff-start option and specify a hash of the commit that will act as a base for comparison, see the Incremental analysis section for details:

docker run \    -v $(pwd):/data/project/ \    -e QODANA_TOKEN="<cloud-project-token>" \    jetbrains/qodana-<image> \    --diff-start=<GIT_START_HASH>
qodana scan \    -e QODANA_TOKEN="<cloud-project-token>" \    --diff-start=<GIT_START_HASH>
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: --diff-start=<GIT_START_HASH> env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --diff-start=<GIT_START_HASH> ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --diff-start=<GIT_START_HASH> --image <image>

In the runner configuration, find the Additional Qodana arguments field and specify the commit hash:

--diff-start=<GIT_START_HASH>

To analyze a set of changes between two commits, employ both --diff-start and --diff-end options:

docker run \    -v $(pwd):/data/project/ \    -e QODANA_TOKEN="<cloud-project-token>" \    jetbrains/qodana-<image> \    --diff-start=<GIT_START_HASH> \    --diff-end=<GIT_END_HASH>
qodana scan \    -e QODANA_TOKEN="<cloud-project-token>" \    --diff-start=<GIT_START_HASH> \    --diff-end=<GIT_END_HASH>
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: | --diff-start=<GIT_START_HASH> --diff-end=<GIT_END_HASH> env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana \ --diff-start=<GIT_START_HASH> \ --diff-end=<GIT_END_HASH> ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | --diff-start=<GIT_START_HASH> --diff-end=<GIT_END_HASH> --image <image>

In the runner configuration, find the Additional Qodana arguments field and specify the commit hashes:

--diff-start=<GIT_START_HASH> --diff-end=<GIT_END_HASH>

Change the Heap size

By default, the Heap size is set to 80% of the host RAM. You can configure this setting using the _JAVA_OPTIONS variable:

docker run \ -v $(pwd):/data/project/ \ -e _JAVA_OPTIONS=-Xmx6g \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image>
qodana scan \ -e _JAVA_OPTIONS=-Xmx6g \ -e QODANA_TOKEN="<cloud-project-token>"
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: -e _JAVA_OPTIONS=-Xmx6g env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project -e _JAVA_OPTIONS=-Xmx6g --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | -e _JAVA_OPTIONS=-Xmx6g --image <image>

In the runner configuration, find the Additional Docker arguments field and configure the heap size using the _JAVA_OPTIONS variable:

-e _JAVA_OPTIONS=-Xmx6g

To learn more about configuring the Heap, see the Heap Tuning Parameters of the Oracle documentation.

Override the idea.properties file

The idea.properties configures the default locations of the IDE files.

You can override the idea.properties file using the IDEA_PROPERTIES variable:

docker run \ -v $(pwd):/data/project/ \ -e IDEA_PROPERTIES=/data/project/idea.properties \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image>
qodana scan \ -e IDEA_PROPERTIES=/data/project/idea.properties \ -e QODANA_TOKEN="<cloud-project-token>"
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: -e IDEA_PROPERTIES=/data/project/idea.properties env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project -e IDEA_PROPERTIES=/data/project/idea.properties --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | -e IDEA_PROPERTIES=/data/project/idea.properties --image <image>

In the runner configuration, find the Additional Docker arguments field and configure the IDEA_PROPERTIES variable:

-e IDEA_PROPERTIES=/data/project/idea.properties

Configure root and non-root users

By default, a Docker container runs under the root user, so Qodana can read project information and write inspection results. Therefore, all files in the results/ directory are owned by the root user after the run.

To overcome this, you can run the container as a regular user:

docker run \ -u $(id -u):$(id -g) \ -v $(pwd):/data/project/ \ -v <results-directory>:/data/results/ \ -e QODANA_TOKEN="<cloud-project-token>" \ jetbrains/qodana-<image>

In this case, the results/ directory on the host should already be created and owned by you. Otherwise, Docker will create it as the root user, and Qodana will not be able to write to it.

TeamCity and Qodana CLI run Qodana using a current non-root user. This can be inconvenient if you wish to install dependencies using the apt tool invoked in the bootstrap section.

To run Qodana as a root user in TeamCity, add the -u root option in the Additional Docker arguments field of the Qodana runner configuration.

To run Qodana CLI as a root user, you can append -u root option to the qodana scan command:

qodana scan -u root
name: Qodana on: workflow_dispatch: pull_request: push: branches: # Specify your branches here - main # The 'main' branch - master # The 'master' branch - 'releases/*' # The release branches jobs: qodana: runs-on: ubuntu-latest permissions: contents: write pull-requests: write checks: write steps: - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: -u root env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
pipeline { environment { QODANA_TOKEN=credentials('qodana-token') } agent { docker { args ''' -v "${WORKSPACE}":/data/project -u root --entrypoint="" ''' image 'jetbrains/qodana-<image>' } } stages { stage('Qodana') { steps { sh ''' qodana ''' } } } }
include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: args: | -u root --image <image>

Git submodules

To analyze repositories that use Git submodules accessed via SSH, you must authenticate Git operations within the Qodana Docker container. In this case, you need to configure an SSH agent and pass an SSH key with access to the submodule into the container as shown in the snippets below:

docker run \ -v $(pwd):/data/project/ \ -v "$SSH_AUTH_SOCK:/tmp/ssh_agent.sock" \ -e SSH_AUTH_SOCK=/tmp/ssh_agent.sock \ -e GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" \ -e QODANA_SKIP_SUBMODULE_UPDATE=true \ jetbrains/qodana-<image> \ --diff-start=<GIT_START_HASH>

This command contains the following options:

Option

Description

-v "$SSH_AUTH_SOCK:/tmp/ssh_agent.sock"

Mount the SSH agent socket into the container

-e SSH_AUTH_SOCK=/tmp/ssh_agent.sock

Set the SSH agent socket environment variable

-e GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no"

Disable strict host key checking for SSH operations

-e QODANA_SKIP_SUBMODULE_UPDATE=true

Skip Git submodule checkout, can be useful if the submodule checkout fails

--diff-start=<GIT_START_HASH>

Commit hash, see the Analyze pull and merge requests chapter for details

qodana scan \ -v "$SSH_AUTH_SOCK:/tmp/ssh_agent.sock" \ -e SSH_AUTH_SOCK=/tmp/ssh_agent.sock \ -e GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" \ -e QODANA_SKIP_SUBMODULE_UPDATE=true \ -e QODANA_TOKEN="<cloud-project-token>" \ --diff-start=<GIT_START_HASH>

This command contains the following options:

Option

Description

-v "$SSH_AUTH_SOCK:/tmp/ssh_agent.sock"

Mount the SSH agent socket into the container

-e SSH_AUTH_SOCK=/tmp/ssh_agent.sock

Set the SSH agent socket environment variable

-e GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no"

Disable strict host key checking for SSH operations

-e QODANA_SKIP_SUBMODULE_UPDATE=true

Skip Git submodule checkout, can be useful if the submodule checkout fails

--diff-start=<GIT_START_HASH>

Commit hash, see the Analyze pull and merge requests chapter for details

jobs: qodana-job: runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@v4 with: submodules: recursive # clones submodules recursively - name: Setup SSH Agent uses: webfactory/ssh-agent@v0.9.0 with: ssh-private-key: ${{ secrets.SUBMODULE_SSH_KEY }} - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2026.2 with: args: | -v ${{ env.SSH_AUTH_SOCK }}:/tmp/ssh_agent.sock -e SSH_AUTH_SOCK=/tmp/ssh_agent.sock -e GIT_SSH_COMMAND=&quot;ssh -o StrictHostKeyChecking=no&quot; -e QODANA_SKIP_SUBMODULE_UPDATE=true upload-result: true pr-mode: 'true' env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}

Here, the args block contains the following options:

Option

Description

-v ${{ env.SSH_AUTH_SOCK }}:/tmp/ssh_agent.sock

Mount the SSH agent socket into the container

-e SSH_AUTH_SOCK=/tmp/ssh_agent.sock

Set the SSH agent socket environment variable

-e GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no

Disable strict host key checking for SSH operations

-e QODANA_SKIP_SUBMODULE_UPDATE=true

Skip Git submodule checkout, can be useful if the submodule checkout fails

include: - component: $CI_SERVER_FQDN/qodana/qodana/qodana-gitlab-ci@v2026.1 inputs: image: <image> args: | -v ${{ env.SSH_AUTH_SOCK }}:/tmp/ssh_agent.sock -e SSH_AUTH_SOCK=/tmp/ssh_agent.sock -e GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" -e QODANA_SKIP_SUBMODULE_UPDATE=true

Here, the args block contains the following options:

Option

Description

-v ${{ env.SSH_AUTH_SOCK }}:/tmp/ssh_agent.sock

Mount the SSH agent socket into the container

-e SSH_AUTH_SOCK=/tmp/ssh_agent.sock

Set the SSH agent socket environment variable

-e GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no

Disable strict host key checking for SSH operations

-e QODANA_SKIP_SUBMODULE_UPDATE=true

Skip Git submodule checkout, can be useful if the submodule checkout fails

Cache in Qodana CLI

Qodana CLI stores files in the <userCacheDir> directory, which is mentioned several times throughout this section. Here is the list of <userCacheDir> directory locations depending on the operating system:

Operating System

Path

macOS

~/Library/Caches/

Linux

~/.cache/

Windows

%LOCALAPPDATA%\

If you run the qodana init command in the project directory, Qodana CLI will let you choose the linter that will be run during inspection, and save the choice in qodana.yaml. Once done, you do not need to specify the linter in the commands, which is shown throughout this section.

The detailed description of the qodana init command is available in the Configure projects section.

Manage plugins

You can specify the plugins that will be downloaded and invoked during inspection using the following YAML configuration:

version: "1.0" linter: <linter> plugins: - id: <plugin.id>

Here, <plugin-id> denotes the Plugin ID from JetBrains Marketplace. For example, for Grazie Professional, the Plugin ID will be com.intellij.grazie.pro. To find the Plugin ID, on the plugin page click the Overview tab and then navigate to the Additional Information section.

Plugin cache is stored in the /data/cache/plugins directory.

To install third-party software required for your plugins, you can:

  • Use the bootstrap key.

  • Develop your custom Dockerfile that starts with FROM jetbrains/qodana.... You can use Qodana Dockerfile examples available on GitHub.

Incorrect Formatting inspection

The IncorrectFormatting inspection consolidates multiple formatting errors contained in a file into a single problem instead of listing every issue separately. Now, a single problem per file is displayed with example snippets to help you fix issues faster.

This feature is available for all linters except Qodana for C/C++, Qodana Community for C/C++, and Qodana Community for .NET.

To start using it, enable the IncorrectFormatting inspection in your Qodana inspection profile configuration, for example:

version: "1.0" linter: <linter> profile: inspections: - inspection: IncorrectFormatting enabled: true

Specify a CMake preset

Customize the Qodana for C/C++ linter by using CMake presets. Invoke presets using the cpp and cmakePreset options:

version: "1.0" linter: <linter> cpp: cmakePreset: my-qodana-preset

Configure Java and Kotlin projects in monorepo

Using the rootJavaProjects key, you can specify which projects should be included in the analysis, for example:

version: "1.0" linter: <linter> rootJavaProjects: - "./gradleProject" - "./mavenModule/pom.xml"

By default, Qodana recursively collects projects from subdirectories and imports them for analysis. This change enables incremental analysis and fixes for projects where the analyzed project and VCS root are different.

Comprehensive configuration examples

version: "1.0" linter: <linter> failThreshold: 0 profile: name: qodana.recommended inspections: - inspection: SomeInspectionId enabled: true - inspection: AnotherInspectionId enabled: true ignore: - "relative/path" - "another/relative/path" - group: ALL ignore: - "asm-test/src/main/java/org" - "benchmarks" - "tools"

In the example above,

  • SomeInspectionId inspection is explicitly enabled for all paths, although it is disabled in the profile

  • AnotherInspectionId inspection is disabled for relative/path and another/relative/path

  • no inspections are conducted over these paths: asm-test/src/main/java/org, benchmarks, tools

The following example combines multiple settings, including a quality gate, inspection profile customization, and path exclusions, which are common in .NET project setups:

version: "1.0" # Run the Qodana for .NET linter in native mode linter: qodana-dotnet withinDocker: false # Set a quality gate: fail if the number of problems exceeds 10 failThreshold: 10 # Use the qodana.recommended profile profile: name: qodana.recommended # Exclude specific paths from the analysis inspections: - group: ALL ignore: - "tests/" - "bin/" - "obj/" # Include an inspection not contained in the qodana.recommended profile - inspection: SomeSpecificInspectionId enabled: true # Restore .NET dependencies bootstrap: |+ dotnet restore

Use this example while configuring the Qodana for JVM linter:

version: "1.0" # Run the Qodana for JVM linter linter: qodana-jvm # Set the JDK version jdk: version: "17" # Include Java projects for analysis rootJavaProjects: - "./gradle-project" - "./maven-module/pom.xml" # Quality gate settings failureConditions: severityThresholds: any: 50 # Fail if total number of problems exceeds 50 critical: 1 # Fail if there is at least 1 critical problem high: 2 # Fail if there are more than 2 high-severity problems testCoverageThresholds: fresh: 80 # Fail if fresh code coverage is below 80% total: 90 # Fail if total code coverage is below 90% # Disable specific inspections profile: inspections: - inspection: CheckDependencyLicenses enabled: false # Disable license audit if not needed # Include custom plugins plugins: - id: com.intellij.grazie.pro
03 September 2026