diff --git a/lib/manager/api.ts b/lib/manager/api.ts
index 71970dc081fce35bfdd63635d9924fe711cc77b2..1731c2500f3fab6391e314382d1a019e3e035331 100644
--- a/lib/manager/api.ts
+++ b/lib/manager/api.ts
@@ -29,6 +29,7 @@ import * as gradleWrapper from './gradle-wrapper';
 import * as helmRequirements from './helm-requirements';
 import * as helmValues from './helm-values';
 import * as helmfile from './helmfile';
+import * as helmsman from './helmsman';
 import * as helmv3 from './helmv3';
 import * as homebrew from './homebrew';
 import * as html from './html';
@@ -97,6 +98,7 @@ api.set('gradle-wrapper', gradleWrapper);
 api.set('helm-requirements', helmRequirements);
 api.set('helm-values', helmValues);
 api.set('helmfile', helmfile);
+api.set('helmsman', helmsman);
 api.set('helmv3', helmv3);
 api.set('homebrew', homebrew);
 api.set('html', html);
diff --git a/lib/manager/helmsman/__fixtures__/empty.yaml b/lib/manager/helmsman/__fixtures__/empty.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d25f01ccddad1d080030cb811e6a7f204d1eb5fd
--- /dev/null
+++ b/lib/manager/helmsman/__fixtures__/empty.yaml
@@ -0,0 +1,6 @@
+namespaces:
+
+helmRepos:
+  test:
+apps:
+  test:
diff --git a/lib/manager/helmsman/__fixtures__/validHelmsfile.yaml b/lib/manager/helmsman/__fixtures__/validHelmsfile.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6bb8d4ee90bb97f118e9b57278c08589cec196a8
--- /dev/null
+++ b/lib/manager/helmsman/__fixtures__/validHelmsfile.yaml
@@ -0,0 +1,78 @@
+namespaces:
+  redis-operator:
+  strimzi:
+  monitoring:
+
+helmRepos:
+  ot-helm: "https://ot-container-kit.github.io/helm-charts/"
+  strimzi: "https://strimzi.io/charts/"
+  open-telemetry: "https://open-telemetry.github.io/opentelemetry-helm-charts"
+  grafana: "https://grafana.github.io/helm-charts"
+  prometheus-community: https://prometheus-community.github.io/helm-charts
+
+apps:
+# valid apps
+  kube-prometheus:
+    enabled: true
+    namespace: monitoring
+    chart: prometheus-community/kube-prometheus-stack
+    version: 19.0.3
+    valuesFiles:
+      -  ./kube-prometheus/values.yaml
+    priority: -90
+  loki:
+    enabled: true
+    namespace: monitoring
+    chart: grafana/loki
+    version: 2.6.0
+    priority: -85
+  tempo:
+    enabled: true
+    namespace: monitoring
+    chart: grafana/tempo
+    version: 0.7.7
+    priority: -80
+  otlp-collector:
+    enabled: true
+    namespace: monitoring
+    chart: open-telemetry/opentelemetry-collector
+    version: 0.6.0
+    valuesFile: ./otlp-collector/values.yaml
+    priority: -75
+  strimzi-operator:
+    enabled: true
+    namespace: strimzi
+    chart: strimzi/strimzi-kafka-operator
+    version: 0.25.0
+
+# missing version
+  strimzi-operator-missing-version:
+    enabled: true
+    namespace: strimzi
+    chart: strimzi/strimzi-kafka-operator
+
+# malformed  chart
+  loki-no-registry-ref:
+    enabled: true
+    namespace: monitoring
+    chart: /loki
+    version: 2.6.0
+  tempo-no-registry-ref:
+    enabled: true
+    namespace: monitoring
+    chart: tempo
+    version: 0.7.7
+  kube-prometheus-no-lookup-name:
+    enabled: true
+    namespace: monitoring
+    chart: prometheus-community/
+    version: 19.0.3
+    valuesFiles:
+      -  ./kube-prometheus/values.yaml
+    priority: -90
+  otlp-collector-no-chart:
+    enabled: true
+    namespace: monitoring
+    version: 0.6.0
+    valuesFile: ./otlp-collector/values.yaml
+    priority: -75
diff --git a/lib/manager/helmsman/__snapshots__/extract.spec.ts.snap b/lib/manager/helmsman/__snapshots__/extract.spec.ts.snap
new file mode 100644
index 0000000000000000000000000000000000000000..240088271e92c0e42089cb16f88d718dd49bdd4d
--- /dev/null
+++ b/lib/manager/helmsman/__snapshots__/extract.spec.ts.snap
@@ -0,0 +1,73 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`manager/helmsman/extract extractPackageFile() extract deps 1`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "19.0.3",
+      "depName": "kube-prometheus",
+      "lookupName": "kube-prometheus-stack",
+      "registryUrls": Array [
+        "https://prometheus-community.github.io/helm-charts",
+      ],
+    },
+    Object {
+      "currentValue": "2.6.0",
+      "depName": "loki",
+      "lookupName": "loki",
+      "registryUrls": Array [
+        "https://grafana.github.io/helm-charts",
+      ],
+    },
+    Object {
+      "currentValue": "0.7.7",
+      "depName": "tempo",
+      "lookupName": "tempo",
+      "registryUrls": Array [
+        "https://grafana.github.io/helm-charts",
+      ],
+    },
+    Object {
+      "currentValue": "0.6.0",
+      "depName": "otlp-collector",
+      "lookupName": "opentelemetry-collector",
+      "registryUrls": Array [
+        "https://open-telemetry.github.io/opentelemetry-helm-charts",
+      ],
+    },
+    Object {
+      "currentValue": "0.25.0",
+      "depName": "strimzi-operator",
+      "lookupName": "strimzi-kafka-operator",
+      "registryUrls": Array [
+        "https://strimzi.io/charts/",
+      ],
+    },
+    Object {
+      "depName": "strimzi-operator-missing-version",
+      "skipReason": "no-version",
+    },
+    Object {
+      "currentValue": "2.6.0",
+      "depName": "loki-no-registry-ref",
+      "lookupName": "loki",
+      "skipReason": "no-repository",
+    },
+    Object {
+      "currentValue": "0.7.7",
+      "depName": "tempo-no-registry-ref",
+      "skipReason": "invalid-url",
+    },
+    Object {
+      "currentValue": "19.0.3",
+      "depName": "kube-prometheus-no-lookup-name",
+      "skipReason": "invalid-name",
+    },
+    Object {
+      "currentValue": "0.6.0",
+      "depName": "otlp-collector-no-chart",
+      "skipReason": "invalid-url",
+    },
+  ],
+}
+`;
diff --git a/lib/manager/helmsman/extract.spec.ts b/lib/manager/helmsman/extract.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3b5b042cf0f7db441bbc4730fbd62cda8955d42e
--- /dev/null
+++ b/lib/manager/helmsman/extract.spec.ts
@@ -0,0 +1,32 @@
+import { loadFixture } from '../../../test/util';
+import { extractPackageFile } from '.';
+
+const multiDepFile = loadFixture('validHelmsfile.yaml');
+const otherYamlFile = loadFixture('empty.yaml');
+
+describe('manager/helmsman/extract', () => {
+  describe('extractPackageFile()', () => {
+    it('returns null if empty', () => {
+      const content = ``;
+      const fileName = 'desired_state.yaml';
+      const result = extractPackageFile(content, fileName, {});
+      expect(result).toBeNull();
+    });
+
+    it('returns null if extracting non helmsman yaml file', () => {
+      const content = otherYamlFile;
+      const fileName = 'requirements.yaml';
+      const result = extractPackageFile(content, fileName, {});
+      expect(result).toBeNull();
+    });
+
+    it('extract deps', () => {
+      const fileName = 'helmsman.yaml';
+      const result = extractPackageFile(multiDepFile, fileName, {});
+      expect(result).not.toBeNull();
+      expect(result.deps).toHaveLength(10);
+      expect(result.deps.filter((value) => value.skipReason)).toHaveLength(5);
+      expect(result).toMatchSnapshot();
+    });
+  });
+});
diff --git a/lib/manager/helmsman/extract.ts b/lib/manager/helmsman/extract.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e81dc2a638e4b6d154ed14a16ea16046d265f4aa
--- /dev/null
+++ b/lib/manager/helmsman/extract.ts
@@ -0,0 +1,80 @@
+import is from '@sindresorhus/is';
+import { load } from 'js-yaml';
+import { logger } from '../../logger';
+import { SkipReason } from '../../types';
+import { regEx } from '../../util/regex';
+import type { ExtractConfig, PackageDependency, PackageFile } from '../types';
+import type { HelmsmanDocument } from './types';
+
+const chartRegex = regEx('^(?<registryRef>[^/]*)/(?<lookupName>[^/]*)$');
+
+function createDep(key: string, doc: HelmsmanDocument): PackageDependency {
+  const dep: PackageDependency = {
+    depName: key,
+  };
+  const anApp = doc.apps[key];
+  if (!anApp) {
+    return null;
+  }
+
+  if (!anApp.version) {
+    dep.skipReason = SkipReason.NoVersion;
+    return dep;
+  }
+  dep.currentValue = anApp.version;
+
+  const regexResult = chartRegex.exec(anApp.chart);
+  if (!regexResult) {
+    dep.skipReason = SkipReason.InvalidUrl;
+    return dep;
+  }
+
+  if (!is.nonEmptyString(regexResult.groups.lookupName)) {
+    dep.skipReason = SkipReason.InvalidName;
+    return dep;
+  }
+  dep.lookupName = regexResult.groups.lookupName;
+
+  const registryUrl = doc.helmRepos[regexResult.groups.registryRef];
+  if (!is.nonEmptyString(registryUrl)) {
+    dep.skipReason = SkipReason.NoRepository;
+    return dep;
+  }
+  dep.registryUrls = [registryUrl];
+
+  return dep;
+}
+
+export function extractPackageFile(
+  content: string,
+  fileName: string,
+  config: ExtractConfig
+): PackageFile | null {
+  try {
+    // TODO: fix me (#9610)
+    const doc = load(content, {
+      json: true,
+    }) as HelmsmanDocument;
+    if (!(doc?.helmRepos && doc.apps)) {
+      logger.debug({ fileName }, 'Missing helmRepos and/or apps keys');
+      return null;
+    }
+
+    const deps = Object.keys(doc.apps)
+      .map((key) => createDep(key, doc))
+      .filter(Boolean); // filter null values
+
+    if (deps.length === 0) {
+      return null;
+    }
+
+    return { deps };
+  } catch (err) /* istanbul ignore next */ {
+    if (err.stack?.startsWith('YAMLException:')) {
+      logger.debug({ err }, 'YAML exception extracting');
+    } else {
+      logger.warn({ err }, 'Error extracting');
+    }
+    return null;
+  }
+}
diff --git a/lib/manager/helmsman/index.ts b/lib/manager/helmsman/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1012c71468dbc48b337475d9c06e8a2591ee13b4
--- /dev/null
+++ b/lib/manager/helmsman/index.ts
@@ -0,0 +1,5 @@
+export { extractPackageFile } from './extract';
+
+export const defaultConfig = {
+  fileMatch: [],
+};
diff --git a/lib/manager/helmsman/readme.md b/lib/manager/helmsman/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..cd1a853a0345b97cbde0716d46ed10910aea2572
--- /dev/null
+++ b/lib/manager/helmsman/readme.md
@@ -0,0 +1,21 @@
+The `helmsman` manager is currently limited and does not support the full feature set of [Helmsman](https://github.com/Praqma/helmsman), read about the limitations below.
+
+### Non-configured fileMatch
+
+By default the `helmsman` manager has an empty array for its `fileMatch` configuration option, because there is no convention for file naming in practice.
+This means that `helmsman` won't search for any files, and you won't get any updates from the manager.
+
+To enable the `helmsman` manager, provide a valid `fileMatch` yourself, for example:
+
+```json
+{
+  "helmsman": {
+    "fileMatch": ["(^|/)desired_state\\.yaml$"]
+  }
+}
+```
+
+### File format
+
+Currently, state files must be in the `.yaml` format.
+The `.toml` format is not supported.
diff --git a/lib/manager/helmsman/types.ts b/lib/manager/helmsman/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2d11c6cca6d7405b16128f42961716d677fe5bbb
--- /dev/null
+++ b/lib/manager/helmsman/types.ts
@@ -0,0 +1,9 @@
+export interface HelmsmanDocument {
+  helmRepos: Record<string, string>;
+  apps: Record<string, HelmsmanApp>;
+}
+
+export interface HelmsmanApp {
+  version?: string;
+  chart?: string;
+}