Gradle build cache behind a corporate proxy
Many companies make builds reach the network through a proxy. This page shows how Gradle's remote build cache behaves behind one: the settings, the ways it goes wrong, and how to tell a proxy problem from a cache problem. Everything was run for real with Gradle 9.8.0 and FosterStack Cache 0.2.1.
What was tested, and what was not
- Tested: a small proxy I wrote for the test (it forwards plain HTTP, tunnels HTTPS with
CONNECT, can refuse a host, and logs every request), the cache on a non-loopback address, once over plain HTTP and once over HTTPS through nginx with a self-signed certificate that Java was told to trust. - Not tested: a real corporate proxy product, a proxy that asks for a password (407), a proxy that inspects and re-signs TLS, automatic proxy configuration files, and the
HTTP_PROXYvariables. Maven is covered on its own page, where it behaves differently: for example it does not skip the proxy for loopback addresses.
The settings
Gradle takes proxy settings from Java system properties. Put them in ~/.gradle/gradle.properties or the project's gradle.properties, with the systemProp. prefix:
systemProp.http.proxyHost=proxy.example.com
systemProp.http.proxyPort=3128
systemProp.https.proxyHost=proxy.example.com
systemProp.https.proxyPort=3128
systemProp.http.nonProxyHosts=cache.internal.example.com|localhost|127.*
Use the https pair for an https:// cache URL and the http pair for plain HTTP. The last line lists hosts that should skip the proxy.
What I saw
For the plain-HTTP rows I set isAllowInsecureProtocol = true, which Gradle requires for non-loopback http://. That is for the test rig only; use HTTPS for real.
| Setup | Result | What the proxy logged |
|---|---|---|
No proxy settings, cache at <cache-ip> | second build FROM-CACHE | nothing (not used) |
| Proxy settings, same plain-HTTP URL | FROM-CACHE | GET http://<cache-ip>:18130/<key>, one per build |
Proxy settings, but the cache URL is 127.0.0.1 | FROM-CACHE | nothing: Java skips the proxy for loopback addresses |
Proxy settings, with the cache's address in nonProxyHosts | FROM-CACHE | nothing |
| Proxy settings, HTTPS cache, tunnel allowed | FROM-CACHE | CONNECT <cache-ip>:18443, one per build |
| Proxy settings, proxy refuses the cache host | the build succeeds but without the cache (log below) | the request, answered 403 |
Three things that catch people
1. Testing against localhost skips the proxy. With the cache at 127.0.0.1, Gradle never used the proxy even though it was configured. A setup that works on your laptop against a local cache proves nothing about the proxy path. Test against the real address.
2. A refusing proxy looks like a cache error. When the proxy answered 403 to the tunnel, Gradle printed:
Could not load entry d4f2de693b561fd2c5d60b17a9232d6f from remote build cache: Loading entry from 'https://<cache-ip>:18443/d4f2de693b561fd2c5d60b17a9232d6f' response status 403: Forbidden
That is the same text you would get from the cache refusing a write with the read-only login (see the status codes). To tell them apart, take Gradle out of it and try the same route with curl:
$ curl -sS -x http://proxy.example.com:3128 https://cache.example.com/healthz
curl: (56) CONNECT tunnel failed, response 403 # the proxy said no
$ curl -sS --noproxy '*' https://cache.example.com/healthz
ok # the cache is fine
I substituted example hostnames in those commands; the two output lines are the real ones, from my test proxy refusing and from the cache answering directly. If the first one fails and the second works, ask whoever runs the proxy to allow the cache's host and port.
3. Over HTTPS the proxy only sees the address. A CONNECT tunnel shows the proxy the host and port and nothing inside, so the password and the cache keys are not visible to it. That also means any rule on the path cannot be applied. This is how the proxy log looked in my run: one CONNECT line per build.
Checklist
- Ask for the cache's host and port to be allowed through the proxy (HTTPS).
- Put the proxy lines in
gradle.properties, with thesystemProp.prefix, and list internal hosts innonProxyHosts. - Use HTTPS to the cache. See a production setup with nginx and TLS.
- If the proxy re-signs TLS, Java will need your company's certificate authority. I did not test that case.
Related: Gradle remote build cache not working and authentication and 401 errors.