diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md
index b0224431ab35f239e703ab56a98014fb704771bc..648cac44b693db6fe4e57d16bf6ff04a425ef7bf 100644
--- a/docs/usage/self-hosted-configuration.md
+++ b/docs/usage/self-hosted-configuration.md
@@ -363,8 +363,15 @@ If this option is not set, Renovate will fallback to 15 minutes.
 ## exposeAllEnv
 
 To keep you safe, Renovate only passes a limited set of environment variables to package managers.
-Confidential data can be leaked if a malicious script enumerates all environment variables.
+If you must expose all environment variables to package managers, you can set this option to `true`.
+
+<!-- prettier-ignore -->
+!!! warning
+    Always consider the security implications of using `exposeAllEnv`!
+    Secrets and other confidential information stored in environment variables could be leaked by a malicious script, that enumerates all environment variables.
+
 Set `exposeAllEnv` to `true` only if you have reviewed, and trust, the repositories which Renovate bot runs against.
+Alternatively, you can use the [`customEnvVariables`](https://docs.renovatebot.com/self-hosted-configuration/#customenvvariables) config option to handpick a set of variables you need to expose.
 
 Setting this to `true` also allows for variable substitution in `.npmrc` files.
 
diff --git a/docs/usage/templates.md b/docs/usage/templates.md
index 8aa2a282be4e7d1e86e6483c5b9e986c25a90619..a7c10f79a06657ab0c970f7fd0120fb28e25cc3b 100644
--- a/docs/usage/templates.md
+++ b/docs/usage/templates.md
@@ -95,3 +95,14 @@ In the example above, it will only show a text if `isMajor=true` and `hasRelease
 Returns `true` if at least one expression is `true`.
 
 `{{#if (or isPatch isSingleVersion}}Small update, safer to merge and release.{{else}}Check out the changelog for all versions before merging!{{/if}}`
+
+## Environment variables
+
+By default, you can only access a handful of basic environment variables like `HOME` or `PATH`.
+This is for security reasons.
+
+`HOME is {{env.HOME}}`
+
+If you're self-hosting Renovate, you can expose additional variables with the [`customEnvVariables`](https://docs.renovatebot.com/self-hosted-configuration/#customenvvariables) config option.
+
+You can also use the [`exposeAllEnv`](https://docs.renovatebot.com/self-hosted-configuration/#exposeallenv) config option to allow all environment variables in templates, but make sure to consider the security implications of giving the scripts unrestricted access to all variables.
diff --git a/lib/util/exec/index.ts b/lib/util/exec/index.ts
index 712e5fa3d06d5ceef31141b2712794d624be0530..ccb99ef13e9a423df1725ef0102751477c8ee170 100644
--- a/lib/util/exec/index.ts
+++ b/lib/util/exec/index.ts
@@ -21,7 +21,7 @@ import type {
   RawExecOptions,
 } from './types';
 
-function getChildEnv({
+export function getChildEnv({
   extraEnv,
   env: forcedEnv = {},
 }: ExecOptions): Record<string, string> {
diff --git a/lib/util/template/index.spec.ts b/lib/util/template/index.spec.ts
index a658988362211be0b911e36b1d07fc67d6e7d813..ec3e4fa6a1ea0a473d2cdf9012119d3240d2317a 100644
--- a/lib/util/template/index.spec.ts
+++ b/lib/util/template/index.spec.ts
@@ -1,7 +1,20 @@
+import { mocked } from '../../../test/util';
 import { getOptions } from '../../config/options';
+import * as _exec from '../exec';
 import * as template from '.';
 
+jest.mock('../exec');
+
+const exec = mocked(_exec);
+
 describe('util/template/index', () => {
+  beforeEach(() => {
+    exec.getChildEnv.mockReturnValue({
+      CUSTOM_FOO: 'foo',
+      HOME: '/root',
+    });
+  });
+
   it('has valid exposed config options', () => {
     const allOptions = getOptions().map((option) => option.name);
     const missingOptions = template.exposedConfigOptions.filter(
@@ -85,6 +98,18 @@ describe('util/template/index', () => {
     expect(output).toBe('foo');
   });
 
+  it('has access to basic environment variables (basicEnvVars)', () => {
+    const userTemplate = 'HOME is {{env.HOME}}';
+    const output = template.compile(userTemplate, {});
+    expect(output).toBe('HOME is /root');
+  });
+
+  it('and has access to custom variables (customEnvVariables)', () => {
+    const userTemplate = 'CUSTOM_FOO is {{env.CUSTOM_FOO}}';
+    const output = template.compile(userTemplate, {});
+    expect(output).toBe('CUSTOM_FOO is foo');
+  });
+
   describe('proxyCompileInput', () => {
     const allowedField = 'body';
     const forbiddenField = 'foobar';
diff --git a/lib/util/template/index.ts b/lib/util/template/index.ts
index 4ff1ae2316e0da4440acad18491b7eaf1836f471..697c253671aaf2095ebc7623d0f1a9a76beac04e 100644
--- a/lib/util/template/index.ts
+++ b/lib/util/template/index.ts
@@ -2,6 +2,7 @@ import is from '@sindresorhus/is';
 import handlebars from 'handlebars';
 import { GlobalConfig } from '../../config/global';
 import { logger } from '../../logger';
+import { getChildEnv } from '../exec';
 
 handlebars.registerHelper('encodeURIComponent', encodeURIComponent);
 handlebars.registerHelper('decodeURIComponent', decodeURIComponent);
@@ -170,6 +171,10 @@ const allowedTemplateFields = new Set([
 
 const compileInputProxyHandler: ProxyHandler<CompileInput> = {
   get(target: CompileInput, prop: keyof CompileInput): unknown {
+    if (prop === 'env') {
+      return target[prop];
+    }
+
     if (!allowedTemplateFields.has(prop)) {
       return undefined;
     }
@@ -202,13 +207,17 @@ export function compile(
   input: CompileInput,
   filterFields = true
 ): string {
-  const data = { ...GlobalConfig.get(), ...input };
+  const env = getChildEnv({});
+  const data = { ...GlobalConfig.get(), ...input, env };
   const filteredInput = filterFields ? proxyCompileInput(data) : data;
   logger.trace({ template, filteredInput }, 'Compiling template');
   if (filterFields) {
     const matches = template.matchAll(templateRegex);
     for (const match of matches) {
       const varNames = match[1].split('.');
+      if (varNames[0] === 'env') {
+        continue;
+      }
       for (const varName of varNames) {
         if (!allowedFieldsList.includes(varName)) {
           logger.info(