diff --git a/runner/fixtures/f4/base/beaver.yaml b/runner/fixtures/f4/base/beaver.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvYmVhdmVyLnlhbWw=
--- /dev/null
+++ b/runner/fixtures/f4/base/beaver.yaml
@@ -0,0 +1,4 @@
+charts:
+  hcl1:
+    type: helm
+    path: ./hcl1
diff --git a/runner/fixtures/f4/base/build.sh b/runner/fixtures/f4/base/build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvYnVpbGQuc2g=
--- /dev/null
+++ b/runner/fixtures/f4/base/build.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+# Local build demo file
+# Demonstrate that leaves dependencies should be built before root ones
+set -ex
+
+pushd ./hcl2
+helm dependency build
+popd
+pushd ./hcl1
+helm dependency build
+helm template .
+popd
+rm hcl*/{charts,Chart.lock} -r
diff --git a/runner/fixtures/f4/base/hcl1/Chart.yaml b/runner/fixtures/f4/base/hcl1/Chart.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvaGNsMS9DaGFydC55YW1s
--- /dev/null
+++ b/runner/fixtures/f4/base/hcl1/Chart.yaml
@@ -0,0 +1,10 @@
+apiVersion: v2
+name: hcl1
+description: A Helm chart for Kubernetes
+type: application
+version: 0.1.0
+appVersion: "1.0.0"
+dependencies:
+- name: hcl2
+  repository: file://../hcl2
+  version: "*"
diff --git a/runner/fixtures/f4/base/hcl1/templates/cm.yaml b/runner/fixtures/f4/base/hcl1/templates/cm.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvaGNsMS90ZW1wbGF0ZXMvY20ueWFtbA==
--- /dev/null
+++ b/runner/fixtures/f4/base/hcl1/templates/cm.yaml
@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: hcl1
+data:
+  helm: level 1
diff --git a/runner/fixtures/f4/base/hcl1/values.yaml b/runner/fixtures/f4/base/hcl1/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvaGNsMS92YWx1ZXMueWFtbA==
--- /dev/null
+++ b/runner/fixtures/f4/base/hcl1/values.yaml
@@ -0,0 +1,1 @@
+nothing: special
diff --git a/runner/fixtures/f4/base/hcl2/Chart.yaml b/runner/fixtures/f4/base/hcl2/Chart.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvaGNsMi9DaGFydC55YW1s
--- /dev/null
+++ b/runner/fixtures/f4/base/hcl2/Chart.yaml
@@ -0,0 +1,10 @@
+apiVersion: v2
+name: hcl2
+description: A Helm chart for Kubernetes
+type: application
+version: 0.1.0
+appVersion: "1.0.0"
+dependencies:
+- name: hcl3
+  repository: file://../hcl3
+  version: "*"
diff --git a/runner/fixtures/f4/base/hcl2/templates/cm.yaml b/runner/fixtures/f4/base/hcl2/templates/cm.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvaGNsMi90ZW1wbGF0ZXMvY20ueWFtbA==
--- /dev/null
+++ b/runner/fixtures/f4/base/hcl2/templates/cm.yaml
@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: hcl2
+data:
+  helm: level 2
diff --git a/runner/fixtures/f4/base/hcl2/values.yaml b/runner/fixtures/f4/base/hcl2/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvaGNsMi92YWx1ZXMueWFtbA==
--- /dev/null
+++ b/runner/fixtures/f4/base/hcl2/values.yaml
@@ -0,0 +1,1 @@
+nothing: special
diff --git a/runner/fixtures/f4/base/hcl3/Chart.yaml b/runner/fixtures/f4/base/hcl3/Chart.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvaGNsMy9DaGFydC55YW1s
--- /dev/null
+++ b/runner/fixtures/f4/base/hcl3/Chart.yaml
@@ -0,0 +1,6 @@
+apiVersion: v2
+name: hcl3
+description: A Helm chart for Kubernetes
+type: application
+version: 0.1.0
+appVersion: "1.0.0"
diff --git a/runner/fixtures/f4/base/hcl3/templates/cm.yaml b/runner/fixtures/f4/base/hcl3/templates/cm.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvaGNsMy90ZW1wbGF0ZXMvY20ueWFtbA==
--- /dev/null
+++ b/runner/fixtures/f4/base/hcl3/templates/cm.yaml
@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: hcl3
+data:
+  helm: level 3
diff --git a/runner/fixtures/f4/base/hcl3/values.yaml b/runner/fixtures/f4/base/hcl3/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2ZpeHR1cmVzL2Y0L2Jhc2UvaGNsMy92YWx1ZXMueWFtbA==
--- /dev/null
+++ b/runner/fixtures/f4/base/hcl3/values.yaml
@@ -0,0 +1,1 @@
+nothing: special
diff --git a/runner/helm.go b/runner/helm.go
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2hlbG0uZ28=
--- /dev/null
+++ b/runner/helm.go
@@ -0,0 +1,143 @@
+package runner
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/go-cmd/cmd"
+	"gopkg.in/yaml.v3"
+)
+
+type HelmDependency struct {
+	Name       string
+	Repository string
+}
+
+type HelmChart struct {
+	Dependencies []HelmDependency
+}
+
+func (c CmdConfig) HelmDependencyBuild() error {
+	paths, err := c.HelmChartsPaths()
+	if err != nil {
+		return err
+	}
+	c.Logger.Debug().Strs("paths", paths).Msg("found helm dependencies")
+	for _, p := range paths {
+		if err := c.HelmBuildDependency(p); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (c CmdConfig) HelmBuildDependency(path string) error {
+	args := []string{"dependency", "build", path}
+	apiCmd := cmd.NewCmd(helmCmd, args...)
+	stdOut, stdErr, err := RunCMD(apiCmd)
+	if err != nil {
+		c.Logger.Err(err).
+			Str("command", helmCmd).
+			Str("args", strings.Join(args, " ")).
+			Str("sdtout", strings.Join(stdOut, "\n")).
+			Str("stderr", strings.Join(stdErr, "\n")).
+			Msg("failed to run command")
+
+		// Error must be pretty printed to end users /!\
+		fmt.Printf("\n%s\n\n", strings.Join(stdErr, "\n"))
+		return fmt.Errorf("failed to run command: %w", err)
+	}
+	c.Logger.Debug().
+		Strs("stdout", stdOut).
+		Str("path", path).
+		Msg("helm dependencies successfully built")
+	return nil
+}
+
+func (c CmdConfig) HelmChartsPaths() ([]string, error) {
+	var allPaths []string
+	for name, chart := range c.Spec.Charts {
+		if chart.Type == "helm" {
+			c.Logger.Debug().
+				Str("chart", name).
+				Str("type", chart.Type).
+				Str("path", chart.Path).
+				Msg("search helm dependencies for")
+			paths, err := c.pathsByChart(chart.Path)
+			if err != nil {
+				return nil, err
+			}
+			for _, p := range paths {
+				// Avoid infinite loop with circular dependencies.
+				// Also improve the performance by templating only
+				// once any given chart in case the dependency is
+				// used multiple times.
+				if !contains(allPaths, p) {
+					allPaths = append(allPaths, p)
+				}
+			}
+		}
+	}
+	return allPaths, nil
+}
+
+func (c CmdConfig) pathsByChart(path string) ([]string, error) {
+	var allPaths []string
+	helmChart, err := getHelmChart(path)
+	if err != nil {
+		return nil, err
+	}
+	for _, dependency := range helmChart.Dependencies {
+		c.Logger.Debug().
+			Str("chart", dependency.Name).
+			Str("repository", dependency.Repository).
+			Msg("found helm dependency")
+
+		if strings.HasPrefix(dependency.Repository, "file://") {
+			subChartPath := filepath.Join(
+				path, strings.TrimPrefix(dependency.Repository, "file://"))
+
+			subChartsDependenciesPaths, err := c.pathsByChart(subChartPath)
+			if err != nil {
+				return nil, err
+			}
+
+			allPaths = append(allPaths, subChartsDependenciesPaths...)
+		}
+	}
+	allPaths = append(allPaths, path)
+	return allPaths, nil
+}
+
+func getHelmChart(path string) (*HelmChart, error) {
+	helmChart := HelmChart{}
+	for _, ext := range []string{"yaml", "yml"} {
+		helmChartFile := filepath.Join(path, fmt.Sprintf("%s.%s", "Chart", ext))
+		fileInfo, err := os.Stat(helmChartFile)
+		if err != nil || fileInfo.IsDir() {
+			continue
+		}
+		helmChartContent, err := os.ReadFile(helmChartFile)
+		if err != nil {
+			return nil, err
+		}
+		err = yaml.Unmarshal(helmChartContent, &helmChart)
+		if err != nil {
+			return nil, err
+		}
+		return &helmChart, nil
+	}
+	return nil, fmt.Errorf("helm Chart.yaml file not found in %s", path)
+}
+
+func contains(s []string, e string) bool {
+	for _, a := range s {
+		if a == e {
+			return true
+		}
+	}
+	return false
+}
diff --git a/runner/helm_test.go b/runner/helm_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL2hlbG1fdGVzdC5nbw==
--- /dev/null
+++ b/runner/helm_test.go
@@ -0,0 +1,42 @@
+package runner_test
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"orus.io/orus-io/beaver/runner"
+	"orus.io/orus-io/beaver/testutils"
+)
+
+func TestHelmDependencyBuild(t *testing.T) {
+	fixtures = "fixtures/f4"
+	tl := testutils.NewTestLogger(t)
+
+	absConfigDir, err := filepath.Abs(fixtures)
+	require.NoError(t, err)
+
+	tmpDir, err := os.MkdirTemp(os.TempDir(), "beaver-")
+	require.NoError(t, err)
+
+	c := runner.NewCmdConfig(tl.Logger(), absConfigDir, "base", false, false, "", "")
+	require.NoError(t, c.Initialize(tmpDir))
+
+	chartsPaths, err := c.HelmChartsPaths()
+	require.NoError(t, err)
+	require.Equal(t, 3, len(chartsPaths))
+	assert.True(t, strings.HasSuffix(chartsPaths[0], "hcl3"))
+	assert.True(t, strings.HasSuffix(chartsPaths[1], "hcl2"))
+	assert.True(t, strings.HasSuffix(chartsPaths[2], "hcl1"))
+
+	buildDir := filepath.Join(fixtures, "build")
+	defer func() {
+		require.NoError(t, runner.CleanDir(buildDir))
+	}()
+	r := runner.NewRunner(c)
+	require.NoError(t, r.Build(tmpDir))
+}
diff --git a/runner/main.go b/runner/main.go
index 47c77405da51921b6b3a85820828fb419ab3082e_cnVubmVyL21haW4uZ28=..ac9b15f6fa5bb32edaa8e465007c66674a162de8_cnVubmVyL21haW4uZ28= 100644
--- a/runner/main.go
+++ b/runner/main.go
@@ -42,6 +42,9 @@
 		return fmt.Errorf("cannot prepare variables: %w", err)
 	}
 	var outputDir string
+	if err := r.config.HelmDependencyBuild(); err != nil {
+		return err
+	}
 	if r.config.Output == "" {
 		w := bytes.NewBuffer([]byte{})
 		if err := hydrateString(r.config.Namespace, w, variables); err != nil {