skip to content

When would you publish to the public scan server versus a self-hosted Develocity instance, and how do you control publishing?

level: seniorimportance: should knowfreq 30%

answer

  1. public = zero infra, third-party, no history
  2. Develocity = private, trends, shared cache
  3. server = "https://..."
  4. publishing.onlyIf { ... }
  5. --scan / --no-scan / uploadInBackground

basics

~20 s

The public server is fine for one-off, non-sensitive debugging. For a team you point scans at a self-hosted Develocity server (server = "https://...") so data stays private and is retained. You control publishing with publishing.onlyIf { ... } and --scan/--no-scan.

solid answer

~50 s

**Public `scans.gradle.com`** is great for ad-hoc, shareable debugging of open-source or non-sensitive builds — zero infra, just `--scan`. But it sends build metadata (dependencies, environment, paths) to a third party and offers no history. For an organization you run **Develocity** and set `develocity { server = "https://develocity.acme.com" }`, keeping data private and gaining searchable history, trends, failure analytics, and a shared remote build cache. You control *whether* a scan publishes with the `buildScan.publishing.onlyIf { ... }` predicate — e.g. publish only on CI, or only on failure: `publishing.onlyIf { !it.buildResult.failures.isEmpty() }`. On the command line `--scan` forces publishing and `--no-scan` disables it for that run. The decision is partly governance: who may publish, where data goes, and what's obfuscated. A common policy is "always publish on CI, opt-in locally, and never to the public server for proprietary code."

code

kotlin · 7 lines
kotlin
develocity {
  server = "https://develocity.acme.com"   // private instead of public server
  buildScan {
    uploadInBackground = System.getenv("CI") == null  // synchronous on CI
    publishing.onlyIf { ctx -> System.getenv("CI") != null || ctx.buildResult.failures.isNotEmpty() }
  }
}

go deeper

for a junior

Know the public server exists and --scan uses it; deeper trade-offs are higher level.

for a middle

Contrast public vs. Develocity at a high level and name --scan/--no-scan.

for a senior

Design publishing policy with publishing.onlyIf, uploadInBackground, and a private server, justified by data sensitivity.

for a principal

Own the org policy: destination governance, obfuscation, centralizing publishing config in a convention plugin, and the cache/observability value of Develocity.

## Two destinations ### Public server (`scans.gradle.com`) - **Pros:** zero setup — `--scan` just works; instantly shareable URL; free. - **Cons:** build metadata leaves your network to a **third-party host**; scans are essentially public-by-URL; **no history/trends**, no private cache, limited retention. - **Use when:** open-source projects, throwaway debugging, reproducing an issue for a public bug report — and the data isn't sensitive. ### Self-hosted Develocity - **Pros:** data stays **inside your network**; **searchable history** and **trends**; **failure analytics**; **Predictive Test Selection**, **Test Distribution**; and a **shared remote build cache** that speeds everyone's builds. - **Cons:** you run/operate the server (licensed product). - **Use when:** any proprietary codebase or any team that wants build observability over time. Point scans at it from `settings.gradle.kts`: ```kotlin develocity { server = "https://develocity.acme.com" buildScan { uploadInBackground = System.getenv("CI") == null } } ``` ## Controlling whether a scan publishes **Programmatic policy** — the `publishing.onlyIf { }` predicate decides per build: ```kotlin develocity { buildScan { publishing.onlyIf { ctx -> ctx.buildResult.failures.isNotEmpty() } // only on failure // or: publishing.onlyIf { System.getenv("CI") != null } // only on CI } } ``` **Command-line overrides:** - `--scan` forces a scan for that invocation. - `--no-scan` disables it for that invocation (overrides config). **Background upload** — `uploadInBackground = true` (default locally) lets the build finish while the scan uploads asynchronously; on CI you often set it `false` so the job doesn't exit before the upload completes and the URL is captured in logs. ## Governance dimension Choosing a destination is a **data-governance** decision: proprietary dependency lists, internal hostnames, usernames, and file paths are all in a scan. Policies to encode: - never publish proprietary builds to the **public** server, - pre-accept terms only for your **own** Develocity instance, - configure **obfuscation** for PII (usernames/IPs/hostnames), - decide **who** is allowed to enable publishing (often centralized in a convention plugin so individual teams can't misroute data). ## Summary mental model Public = quick, shareable, non-sensitive, no memory. Develocity = private, retained, analyzable, cached — the right default for a company. `onlyIf` + `--scan`/`--no-scan` are your switches for *when*.

  • Why might you set `uploadInBackground = false` on CI?
    So the build doesn't terminate before the scan finishes uploading. Synchronous upload guarantees the scan URL is produced and captured in the CI logs instead of being lost when the job exits.
  • How do you publish a scan only when a build fails?
    Use `publishing.onlyIf { ctx -> ctx.buildResult.failures.isNotEmpty() }` in the buildScan block, so successful builds don't generate noise and you keep scans for the failures you actually need to debug.
  • What's a key advantage of Develocity over the public server beyond privacy?
    Searchable history and trends plus a shared remote build cache: you can track build-time regressions over weeks and let the whole team reuse cached task outputs, which the stateless public server can't offer.

saying these in an interview costs you the question

  • Recommending the public scan server for a proprietary codebase — it ships metadata to a third party.
  • Confusing `--no-scan` (disable for this run) with disabling the plugin; and assuming background upload always completes on CI.

context