diff --git a/docs/usage/private-modules.md b/docs/usage/private-modules.md
index e12ef14ef87e2221513f0b112efab9d54cc4d390..76611a006dc269b588b51e6143bb0c933170733a 100644
--- a/docs/usage/private-modules.md
+++ b/docs/usage/private-modules.md
@@ -211,3 +211,67 @@ For instructions on this, see the above section on encrypting secrets for the Wh
 - Replace the existing public key in the HTML with the public key you generated in the step prior
 - Use the resulting HTML encrypt page to encrypt secrets for your app before adding them to user/repository config
 - Configure the app to run with `privateKey` set to the private key you generated above
+
+### hostRules configuration using environment variables
+
+Self-hosted users can use environment variables to configure the most common types of `hostRules` for authentication.
+
+The format of the environment variables must be all upper-case and follow:
+
+- Datasource name (e.g. `NPM`, `PYPI`)
+- Underscore (`_`)
+- `matchHost`
+- Underscore (`_`)
+- Field name (`TOKEN`, `USER_NAME`, or `PASSWORD`)
+
+Hyphens (`-`) in datasource or host name must be replaced with double underscores (`__`).
+Periods (`.`) in host names must be replaced with a single underscore (`_`).
+
+Examples:
+
+`NPM_REGISTRY_NPMJS_ORG_TOKEN=abc123`:
+
+```json
+{
+  "hostRules": [
+    {
+      "hostType": "npm",
+      "matchHost": "registry.npmjs.org",
+      "token": "abc123"
+    }
+  ]
+}
+```
+
+`GITLAB__TAGS_CODE__HOST_COMPANY_COM_USERNAME=bot GITLAB__TAGS_CODE__HOST_COMPANY_COM_PASSWORD=botpass123`:
+
+```json
+{
+  "hostRules": [
+    {
+      "hostType": "gitlab-tags",
+      "matchHost": "code-host.company.com",
+      "username": "bot",
+      "password": "botpass123"
+    }
+  ]
+}
+```
+
+It's also possible to skip the host part, and just have datasource + credentials.
+
+Example:
+
+`DOCKER_USERNAME=bot DOCKER_PASSWORD=botpass123`:
+
+```json
+{
+  "hostRules": [
+    {
+      "hostType": "docker",
+      "username": "bot",
+      "password": "botpass123"
+    }
+  ]
+}
+```
diff --git a/lib/config/__snapshots__/env.spec.ts.snap b/lib/config/__snapshots__/env.spec.ts.snap
index c33d6580cc22dda0ab1dc0e69ad02f18bf839dd4..98f9c387eb70ce054d522114a8dadf1c10052b6f 100644
--- a/lib/config/__snapshots__/env.spec.ts.snap
+++ b/lib/config/__snapshots__/env.spec.ts.snap
@@ -82,6 +82,17 @@ Object {
 }
 `;
 
+exports[`config/env .getConfig(env) supports datasource env token 1`] = `
+Object {
+  "hostRules": Array [
+    Object {
+      "hostType": "pypi",
+      "token": "some-token",
+    },
+  ],
+}
+`;
+
 exports[`config/env .getConfig(env) supports docker username/password 1`] = `
 Object {
   "hostRules": Array [
@@ -93,3 +104,31 @@ Object {
   ],
 }
 `;
+
+exports[`config/env .getConfig(env) supports domain and host names with case insensitivity 1`] = `
+Object {
+  "hostRules": Array [
+    Object {
+      "hostType": "github-tags",
+      "matchHost": "github.com",
+      "token": "some-token",
+    },
+    Object {
+      "hostType": "pypi",
+      "matchHost": "my.custom.host",
+      "password": "some-password",
+    },
+  ],
+}
+`;
+
+exports[`config/env .getConfig(env) supports password-only 1`] = `
+Object {
+  "hostRules": Array [
+    Object {
+      "hostType": "npm",
+      "password": "some-password",
+    },
+  ],
+}
+`;
diff --git a/lib/config/env.spec.ts b/lib/config/env.spec.ts
index 0f395003cad4bc8c7a5a18788264f7a92dacca73..acb608f5ab7ef4878b2f03c35bca60c975865402 100644
--- a/lib/config/env.spec.ts
+++ b/lib/config/env.spec.ts
@@ -99,6 +99,33 @@ describe(getName(), () => {
       };
       expect(env.getConfig(envParam)).toMatchSnapshot();
     });
+    it('supports password-only', () => {
+      const envParam: NodeJS.ProcessEnv = {
+        NPM_PASSWORD: 'some-password',
+      };
+      expect(env.getConfig(envParam)).toMatchSnapshot();
+    });
+    it('supports domain and host names with case insensitivity', () => {
+      const envParam: NodeJS.ProcessEnv = {
+        GITHUB__TAGS_GITHUB_COM_TOKEN: 'some-token',
+        pypi_my_CUSTOM_HOST_passWORD: 'some-password',
+      };
+      const res = env.getConfig(envParam);
+      expect(res).toMatchSnapshot();
+      expect(res.hostRules).toHaveLength(2);
+    });
+    it('supports datasource env token', () => {
+      const envParam: NodeJS.ProcessEnv = {
+        PYPI_TOKEN: 'some-token',
+      };
+      expect(env.getConfig(envParam)).toMatchSnapshot();
+    });
+    it('rejects incomplete datasource env token', () => {
+      const envParam: NodeJS.ProcessEnv = {
+        PYPI_FOO_TOKEN: 'some-token',
+      };
+      expect(env.getConfig(envParam).hostRules).toHaveLength(0);
+    });
     it('supports Bitbucket token', () => {
       const envParam: NodeJS.ProcessEnv = {
         RENOVATE_PLATFORM: PLATFORM_TYPE_BITBUCKET,
diff --git a/lib/config/env.ts b/lib/config/env.ts
index fd960cbb75e446458838118614c44b48002a2704..9858f3ff8ff359fbdb2967553355ea22b5a9dfda 100644
--- a/lib/config/env.ts
+++ b/lib/config/env.ts
@@ -1,8 +1,9 @@
 import is from '@sindresorhus/is';
 
 import { PLATFORM_TYPE_GITHUB } from '../constants/platforms';
-import * as datasourceDocker from '../datasource/docker';
+import { getDatasourceList } from '../datasource';
 import { logger } from '../logger';
+import type { HostRule } from '../types';
 import { getOptions } from './definitions';
 import type { GlobalConfig, RenovateOptions } from './types';
 
@@ -86,14 +87,52 @@ export function getConfig(env: NodeJS.ProcessEnv): GlobalConfig {
     });
   }
 
-  if (env.DOCKER_USERNAME && env.DOCKER_PASSWORD) {
-    config.hostRules.push({
-      hostType: datasourceDocker.id,
-      username: env.DOCKER_USERNAME,
-      password: env.DOCKER_PASSWORD,
-    });
+  const datasources = new Set(getDatasourceList());
+  const fields = ['token', 'username', 'password'];
+
+  const hostRules: HostRule[] = [];
+
+  for (const envName of Object.keys(env).sort()) {
+    // Double underscore __ is used in place of hyphen -
+    const splitEnv = envName.toLowerCase().replace('__', '-').split('_');
+    const hostType = splitEnv.shift();
+    if (datasources.has(hostType)) {
+      const suffix = splitEnv.pop();
+      if (fields.includes(suffix)) {
+        let matchHost: string;
+        const rule: HostRule = {};
+        rule[suffix] = env[envName];
+        if (splitEnv.length === 0) {
+          // host-less rule
+        } else if (splitEnv.length === 1) {
+          logger.warn(`Cannot parse ${envName} env`);
+          continue; // eslint-disable-line no-continue
+        } else {
+          matchHost = splitEnv.join('.');
+        }
+        const existingRule = hostRules.find(
+          (hr) => hr.hostType === hostType && hr.matchHost === matchHost
+        );
+        if (existingRule) {
+          // Add current field to existing rule
+          existingRule[suffix] = env[envName];
+        } else {
+          // Create a new rule
+          const newRule: HostRule = {
+            hostType,
+          };
+          if (matchHost) {
+            newRule.matchHost = matchHost;
+          }
+          newRule[suffix] = env[envName];
+          hostRules.push(newRule);
+        }
+      }
+    }
   }
 
+  config.hostRules = [...config.hostRules, ...hostRules];
+
   // These env vars are deprecated and deleted to make sure they're not used
   const unsupportedEnv = [
     'BITBUCKET_TOKEN',