diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d02bd1a..e0b6d469 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,21 @@
 # Changelog
 
+## Unreleased
+
+- Add support to marking tasks and includes as internal, which will hide them
+  from `--list` and `--list-all`
+  ([#818](https://github.com/go-task/task/pull/818)).
+
+## v3.15.2 - 2022-09-08
+
+- Fix error when using variable in `env:` introduced in the previous release
+  ([#858](https://github.com/go-task/task/issues/858), [#866](https://github.com/go-task/task/pull/866)).
+- Fix handling of `CLI_ARGS` (`--`) in Bash completion
+  ([#863](https://github.com/go-task/task/pull/863)).
+- On zsh completion, add ability to replace `--list-all` with `--list` as
+  already possible on the Bash completion
+  ([#861](https://github.com/go-task/task/pull/861)).
+
 ## v3.15.0 - 2022-09-03
 
 - Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
diff --git a/Taskfile.yml b/Taskfile.yml
index c218ff3e..16fe4dc5 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -80,6 +80,19 @@ tasks:
     cmds:
       - goreleaser --snapshot --rm-dist
 
+  docs:changelog:
+    desc: Copy CHANGELOG.md to the documentation website
+    vars:
+      FILE: docs/docs/changelog.md
+    cmds:
+      - rm {{.FILE}}
+      - 'echo "---" >> {{.FILE}}'
+      - 'echo "slug: /changelog/" >> {{.FILE}}'
+      - 'echo "sidebar_position: 6" >> {{.FILE}}'
+      - 'echo "---" >> {{.FILE}}'
+      - 'echo "" >> {{.FILE}}'
+      - 'cat CHANGELOG.md >> {{.FILE}}'
+
   packages:
     cmds:
       - echo '{{.GO_PACKAGES}}'
diff --git a/completion/bash/task.bash b/completion/bash/task.bash
index f1622111..de93e4c8 100644
--- a/completion/bash/task.bash
+++ b/completion/bash/task.bash
@@ -7,6 +7,18 @@ function _task()
   local cur prev words cword
   _init_completion -n : || return
 
+  # Check for `--` within command-line and quit or strip suffix.
+  local i
+  for i in "${!words[@]}"; do
+    if [ "${words[$i]}" == "--" ]; then
+      # Do not complete words following `--` passed to CLI_ARGS.
+      [ $cword -gt $i ] && return
+      # Remove the words following `--` to not put --list in CLI_ARGS.
+      words=( "${words[@]:0:$i}" )
+      break
+    fi
+  done
+
   # Handle special arguments of options.
   case "$prev" in
     -d|--dir)
@@ -33,7 +45,7 @@ function _task()
   esac
 
   # Prepare task name completions.
-  local tasks=( $( "${COMP_WORDS[@]}" --silent $_GO_TASK_COMPLETION_LIST_OPTION 2> /dev/null ) )
+  local tasks=( $( "${words[@]}" --silent $_GO_TASK_COMPLETION_LIST_OPTION 2> /dev/null ) )
   COMPREPLY=( $( compgen -W "${tasks[*]}" -- "$cur" ) )
 
   # Post-process because task names might contain colons.
diff --git a/completion/zsh/_task b/completion/zsh/_task
index c36a2093..95cde770 100755
--- a/completion/zsh/_task
+++ b/completion/zsh/_task
@@ -3,6 +3,8 @@
 local context state state_descr line
 typeset -A opt_args
 
+_GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}"
+
 # Listing commands from Taskfile.yml
 function __task_list() {
     local -a scripts cmd
@@ -27,7 +29,7 @@ function __task_list() {
     (( enabled )) || return 0
 
     scripts=()
-    for item in "${(@)${(f)$("${cmd[@]}" --list-all)}[2,-1]#\* }"; do
+    for item in "${(@)${(f)$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION)}[2,-1]#\* }"; do
         task="${item%%:[[:space:]]*}"
         desc="${item##[^[:space:]]##[[:space:]]##}"
         scripts+=( "${task//:/\\:}:$desc" )
diff --git a/docs/docs/api_reference.md b/docs/docs/api_reference.md
index 39433ef9..e67b721a 100644
--- a/docs/docs/api_reference.md
+++ b/docs/docs/api_reference.md
@@ -97,6 +97,7 @@ Some environment variables can be overriden to adjust Task behavior.
 | `taskfile` | `string` | | The path for the Taskfile or directory to be included. If a directory, Task will look for files named `Taskfile.yml` or `Taskfile.yaml` inside that directory. If a relative path, resolved relative to the directory containing the including Taskfile. |
 | `dir` | `string` | The parent Taskfile directory | The working directory of the included tasks when run. |
 | `optional` | `bool` | `false` | If `true`, no errors will be thrown if the specified file does not exist. |
+| `internal` | `bool` | `false` | If `true`, tasks will be omitted from both `--list` and `--list-all`. |
 
 :::info
 
@@ -119,6 +120,7 @@ includes:
 | `dir` | `string` | | The current directory which this task should run. |
 | `method` | `string` | `checksum` | Method used by this task. Default to the one declared globally or `checksum`. Available options: `checksum`, `timestamp` and `none` |
 | `silent` | `bool` | `false` | Skips some output for this task. Note that STDOUT and STDERR of the commands will still be redirected. |
+| `internal` | `bool` | `false` | If `true`, omit this task from both `--list` and `--list-all`. |
 | `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
 | `prefix` | `string` | | Allows to override the prefix print before the STDOUT. Only relevant when using the `prefixed` output mode. |
 | `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the commands. |
diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md
index 1022bba7..4b9aa4ec 100644
--- a/docs/docs/changelog.md
+++ b/docs/docs/changelog.md
@@ -5,6 +5,16 @@ sidebar_position: 6
 
 # Changelog
 
+## v3.15.2 - 2022-09-08
+
+- Fix error when using variable in `env:` introduced in the previous release
+  ([#858](https://github.com/go-task/task/issues/858), [#866](https://github.com/go-task/task/pull/866)).
+- Fix handling of `CLI_ARGS` (`--`) in Bash completion
+  ([#863](https://github.com/go-task/task/pull/863)).
+- On zsh completion, add ability to replace `--list-all` with `--list` as
+  already possible on the Bash completion
+  ([#861](https://github.com/go-task/task/pull/861)).
+
 ## v3.15.0 - 2022-09-03
 
 - Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
diff --git a/docs/docs/usage.md b/docs/docs/usage.md
index e6dbe7fa..f79f10bf 100644
--- a/docs/docs/usage.md
+++ b/docs/docs/usage.md
@@ -194,6 +194,22 @@ tasks:
       - echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist"
 ```
 
+### Internal includes
+
+Includes marked as internal will set all the tasks of the included file to be
+internal as well (see the [Internal tasks](#internal-tasks) section below).
+This is useful when including utility tasks that are not intended to be used
+directly by the user.
+
+```yaml
+version: '3'
+
+includes:
+  tests:
+    taskfile: ./taskfiles/Utils.yml
+    internal: true
+```
+
 ### Vars of included Taskfiles
 
 You can also specify variables when including a Taskfile. This may be useful
@@ -223,6 +239,29 @@ use the [default function](https://go-task.github.io/slim-sprig/defaults.html):
 
 :::
 
+## Internal tasks
+
+Internal tasks are tasks that cannot be called directly by the user. They will
+not appear in the output when running `task --list|--list-all`. Other tasks may
+call internal tasks in the usual way. This is useful for creating reusable,
+function-like tasks that have no useful purpose on the command line.
+
+```yaml
+version: '3'
+
+tasks:
+  build-image-1:
+    cmds:
+      - task: build-image
+        vars:
+          DOCKER_IMAGE: image-1
+
+  build-image:
+    internal: true
+    cmds:
+      - docker build -t {{.DOCKER_IMAGE}} .
+```
+
 ## Task directory
 
 By default, tasks will be executed in the directory where the Taskfile is
diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js
index 5bc7dc94..5a3bd8d7 100644
--- a/docs/docusaurus.config.js
+++ b/docs/docusaurus.config.js
@@ -1,8 +1,8 @@
 // @ts-check
 // Note: type annotations allow type checking and IDEs autocompletion
 
-const lightCodeTheme = require('prism-react-renderer/themes/github');
-const darkCodeTheme = require('prism-react-renderer/themes/dracula');
+const lightCodeTheme = require('./src/themes/prismLight');
+const darkCodeTheme = require('./src/themes/prismDark');
 
 const GITHUB_URL = 'https://github.com/go-task/task';
 const TWITTER_URL = 'https://twitter.com/taskfiledev';
diff --git a/docs/src/themes/prismDark.js b/docs/src/themes/prismDark.js
new file mode 100644
index 00000000..84af2732
--- /dev/null
+++ b/docs/src/themes/prismDark.js
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const darkTheme = require('prism-react-renderer/themes/vsDark/index.cjs.js');
+
+module.exports = {
+  plain: {
+    color: '#D4D4D4',
+    backgroundColor: '#212121'
+  },
+  styles: [
+    ...darkTheme.styles,
+    {
+      types: ['title'],
+      style: {
+        color: '#569CD6',
+        fontWeight: 'bold'
+      }
+    },
+    {
+      types: ['property', 'parameter'],
+      style: {
+        color: '#9CDCFE'
+      }
+    },
+    {
+      types: ['script'],
+      style: {
+        color: '#D4D4D4'
+      }
+    },
+    {
+      types: ['boolean', 'arrow', 'atrule', 'tag'],
+      style: {
+        color: '#569CD6'
+      }
+    },
+    {
+      types: ['number', 'color', 'unit'],
+      style: {
+        color: '#B5CEA8'
+      }
+    },
+    {
+      types: ['font-matter'],
+      style: {
+        color: '#CE9178'
+      }
+    },
+    {
+      types: ['keyword', 'rule'],
+      style: {
+        color: '#C586C0'
+      }
+    },
+    {
+      types: ['regex'],
+      style: {
+        color: '#D16969'
+      }
+    },
+    {
+      types: ['maybe-class-name'],
+      style: {
+        color: '#4EC9B0'
+      }
+    },
+    {
+      types: ['constant'],
+      style: {
+        color: '#4FC1FF'
+      }
+    }
+  ]
+};
diff --git a/docs/src/themes/prismLight.js b/docs/src/themes/prismLight.js
new file mode 100644
index 00000000..062055d8
--- /dev/null
+++ b/docs/src/themes/prismLight.js
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const lightTheme = require('prism-react-renderer/themes/github/index.cjs.js');
+
+module.exports = {
+  ...lightTheme,
+  styles: [
+    ...lightTheme.styles,
+    {
+      types: ['title'],
+      style: {
+        color: '#0550AE',
+        fontWeight: 'bold'
+      }
+    },
+    {
+      types: ['parameter'],
+      style: {
+        color: '#953800'
+      }
+    },
+    {
+      types: ['boolean', 'rule', 'color', 'number', 'constant', 'property'],
+      style: {
+        color: '#005CC5'
+      }
+    },
+    {
+      types: ['atrule', 'tag'],
+      style: {
+        color: '#22863A'
+      }
+    },
+    {
+      types: ['script'],
+      style: {
+        color: '#24292E'
+      }
+    },
+    {
+      types: ['operator', 'unit', 'rule'],
+      style: {
+        color: '#D73A49'
+      }
+    },
+    {
+      types: ['font-matter', 'string', 'attr-value'],
+      style: {
+        color: '#C6105F'
+      }
+    },
+    {
+      types: ['class-name'],
+      style: {
+        color: '#116329'
+      }
+    },
+    {
+      types: ['attr-name'],
+      style: {
+        color: '#0550AE'
+      }
+    },
+    {
+      types: ['keyword'],
+      style: {
+        color: '#CF222E'
+      }
+    },
+    {
+      types: ['function'],
+      style: {
+        color: '#8250DF'
+      }
+    },
+    {
+      types: ['selector'],
+      style: {
+        color: '#6F42C1'
+      }
+    },
+    {
+      types: ['variable'],
+      style: {
+        color: '#E36209'
+      }
+    },
+    {
+      types: ['comment'],
+      style: {
+        color: '#6B6B6B'
+      }
+    }
+  ]
+};
diff --git a/docs/static/img/og-image.png b/docs/static/img/og-image.png
index d73f00b2..ea43f9be 100644
Binary files a/docs/static/img/og-image.png and b/docs/static/img/og-image.png differ
diff --git a/docs/static/js/carbon.js b/docs/static/js/carbon.js
index e0fa2871..f18744a2 100644
--- a/docs/static/js/carbon.js
+++ b/docs/static/js/carbon.js
@@ -1,6 +1,6 @@
 (function () {
   function attachAd() {
-    const el = document.createElement('script');
+    var el = document.createElement('script');
     el.setAttribute('type', 'text/javascript');
     el.setAttribute('id', '_carbonads_js');
     el.setAttribute(
@@ -9,7 +9,7 @@
     );
     el.setAttribute('async', 'async');
 
-    const wrapper = document.getElementById('sidebar-ads');
+    var wrapper = document.getElementById('sidebar-ads');
     wrapper.innerHTML = '';
     wrapper.appendChild(el);
   }
@@ -17,8 +17,13 @@
   setTimeout(function () {
     attachAd();
 
-    window.addEventListener('popstate', function () {
-      attachAd();
-    });
+    var currentPath = window.location.pathname;
+
+    setInterval(function () {
+      if (currentPath !== window.location.pathname) {
+        currentPath = window.location.pathname;
+        attachAd();
+      }
+    }, 1000);
   }, 1000);
 })();
diff --git a/errors.go b/errors.go
index 483ad565..cc2e65ed 100644
--- a/errors.go
+++ b/errors.go
@@ -20,6 +20,14 @@ func (err *taskNotFoundError) Error() string {
 	return fmt.Sprintf(`task: Task %q not found`, err.taskName)
 }
 
+type taskInternalError struct {
+	taskName string
+}
+
+func (err *taskInternalError) Error() string {
+	return fmt.Sprintf(`task: Task "%s" is internal`, err.taskName)
+}
+
 type TaskRunError struct {
 	taskName string
 	err      error
diff --git a/help.go b/help.go
index 0dc99718..85ce0ce7 100644
--- a/help.go
+++ b/help.go
@@ -52,7 +52,9 @@ func (e *Executor) printTasks(listAll bool) {
 func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
 	tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
 	for _, task := range e.Taskfile.Tasks {
-		tasks = append(tasks, task)
+		if !task.Internal {
+			tasks = append(tasks, task)
+		}
 	}
 	sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
 	return
@@ -61,7 +63,7 @@ func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
 func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
 	tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
 	for _, task := range e.Taskfile.Tasks {
-		if task.Desc != "" {
+		if !task.Internal && task.Desc != "" {
 			compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
 			if err == nil {
 				task = compiledTask
@@ -92,7 +94,7 @@ func (e *Executor) ListTaskNames(allTasks bool) {
 	// create a string slice from all map values (*taskfile.Task)
 	s := make([]string, 0, len(e.Taskfile.Tasks))
 	for _, t := range e.Taskfile.Tasks {
-		if allTasks || t.Desc != "" {
+		if (allTasks || t.Desc != "") && !t.Internal {
 			s = append(s, strings.TrimRight(t.Task, ":"))
 		}
 	}
diff --git a/task.go b/task.go
index e3b3b6d1..0a075161 100644
--- a/task.go
+++ b/task.go
@@ -65,11 +65,16 @@ type Executor struct {
 func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
 	// check if given tasks exist
 	for _, c := range calls {
-		if _, ok := e.Taskfile.Tasks[c.Task]; !ok {
+		t, ok := e.Taskfile.Tasks[c.Task]
+		if !ok {
 			// FIXME: move to the main package
 			e.ListTasksWithDesc()
 			return &taskNotFoundError{taskName: c.Task}
 		}
+		if t.Internal {
+			e.ListTasksWithDesc()
+			return &taskInternalError{taskName: c.Task}
+		}
 	}
 
 	if e.Summary {
diff --git a/task_test.go b/task_test.go
index 7ce4e19a..ca3e02d8 100644
--- a/task_test.go
+++ b/task_test.go
@@ -959,6 +959,86 @@ func TestIncludesRelativePath(t *testing.T) {
 	assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
 }
 
+func TestIncludesInternal(t *testing.T) {
+	const dir = "testdata/internal_task"
+	tests := []struct {
+		name           string
+		task           string
+		expectedErr    bool
+		expectedOutput string
+	}{
+		{"included internal task via task", "task-1", false, "Hello, World!\n"},
+		{"included internal task via dep", "task-2", false, "Hello, World!\n"},
+		{
+			"included internal direct",
+			"included:task-3",
+			true,
+			"task: No tasks with description available. Try --list-all to list all tasks\n",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			var buff bytes.Buffer
+			e := task.Executor{
+				Dir:    dir,
+				Stdout: &buff,
+				Stderr: &buff,
+				Silent: true,
+			}
+			assert.NoError(t, e.Setup())
+
+			err := e.Run(context.Background(), taskfile.Call{Task: test.task})
+			if test.expectedErr {
+				assert.Error(t, err, test.expectedErr)
+			} else {
+				assert.NoError(t, err)
+			}
+			assert.Equal(t, test.expectedOutput, buff.String())
+		})
+	}
+}
+
+func TestInternalTask(t *testing.T) {
+	const dir = "testdata/internal_task"
+	tests := []struct {
+		name           string
+		task           string
+		expectedErr    bool
+		expectedOutput string
+	}{
+		{"internal task via task", "task-1", false, "Hello, World!\n"},
+		{"internal task via dep", "task-2", false, "Hello, World!\n"},
+		{
+			"internal direct",
+			"task-3",
+			true,
+			"task: No tasks with description available. Try --list-all to list all tasks\n",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			var buff bytes.Buffer
+			e := task.Executor{
+				Dir:    dir,
+				Stdout: &buff,
+				Stderr: &buff,
+				Silent: true,
+			}
+			assert.NoError(t, e.Setup())
+
+			err := e.Run(context.Background(), taskfile.Call{Task: test.task})
+			if test.expectedErr {
+				assert.Error(t, err, test.expectedErr)
+			} else {
+				assert.NoError(t, err)
+			}
+			assert.Equal(t, test.expectedOutput, buff.String())
+		})
+	}
+}
+
 func TestSupportedFileNames(t *testing.T) {
 	fileNames := []string{
 		"Taskfile.yml",
diff --git a/taskfile/included_taskfile.go b/taskfile/included_taskfile.go
index ec4ade08..fe83bd7d 100644
--- a/taskfile/included_taskfile.go
+++ b/taskfile/included_taskfile.go
@@ -16,6 +16,7 @@ type IncludedTaskfile struct {
 	Taskfile       string
 	Dir            string
 	Optional       bool
+	Internal       bool
 	AdvancedImport bool
 	Vars           *Vars
 	BaseDir        string // The directory from which the including taskfile was loaded; used to resolve relative paths
@@ -101,6 +102,7 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
 		Taskfile string
 		Dir      string
 		Optional bool
+		Internal bool
 		Vars     *Vars
 	}
 	if err := unmarshal(&includedTaskfile); err != nil {
@@ -109,6 +111,7 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
 	it.Taskfile = includedTaskfile.Taskfile
 	it.Dir = includedTaskfile.Dir
 	it.Optional = includedTaskfile.Optional
+	it.Internal = includedTaskfile.Internal
 	it.AdvancedImport = true
 	it.Vars = includedTaskfile.Vars
 	return nil
diff --git a/taskfile/merge.go b/taskfile/merge.go
index a5731c71..baebad0e 100644
--- a/taskfile/merge.go
+++ b/taskfile/merge.go
@@ -9,7 +9,7 @@ import (
 const NamespaceSeparator = ":"
 
 // Merge merges the second Taskfile into the first
-func Merge(t1, t2 *Taskfile, namespaces ...string) error {
+func Merge(t1, t2 *Taskfile, internal bool, namespaces ...string) error {
 	if t1.Version != t2.Version {
 		return fmt.Errorf(`task: Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version)
 	}
@@ -43,6 +43,8 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
 		// have serious side-effects in the future, since we're editing
 		// the original references instead of deep copying them.
 
+		v.Internal = v.Internal || internal
+
 		t1.Tasks[taskNameWithNamespace(k, namespaces...)] = v
 
 		for _, dep := range v.Deps {
diff --git a/taskfile/read/dotenv.go b/taskfile/read/dotenv.go
index 70b9dc1d..caed7b1c 100644
--- a/taskfile/read/dotenv.go
+++ b/taskfile/read/dotenv.go
@@ -27,6 +27,9 @@ func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.V
 
 	for _, dotEnvPath := range tf.Dotenv {
 		dotEnvPath = tr.Replace(dotEnvPath)
+		if dotEnvPath == "" {
+			continue
+		}
 		dotEnvPath = filepathext.SmartJoin(dir, dotEnvPath)
 
 		if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
diff --git a/taskfile/read/taskfile.go b/taskfile/read/taskfile.go
index 64f68e0c..220b66c4 100644
--- a/taskfile/read/taskfile.go
+++ b/taskfile/read/taskfile.go
@@ -78,6 +78,7 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
 				Taskfile:       tr.Replace(includedTask.Taskfile),
 				Dir:            tr.Replace(includedTask.Dir),
 				Optional:       includedTask.Optional,
+				Internal:       includedTask.Internal,
 				AdvancedImport: includedTask.AdvancedImport,
 				Vars:           includedTask.Vars,
 				BaseDir:        includedTask.BaseDir,
@@ -148,7 +149,7 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
 			}
 		}
 
-		if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
+		if err = taskfile.Merge(t, includedTaskfile, includedTask.Internal, namespace); err != nil {
 			return err
 		}
 		return nil
@@ -164,7 +165,7 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
 			if err != nil {
 				return nil, err
 			}
-			if err = taskfile.Merge(t, osTaskfile); err != nil {
+			if err = taskfile.Merge(t, osTaskfile, false); err != nil {
 				return nil, err
 			}
 		}
diff --git a/taskfile/task.go b/taskfile/task.go
index 6fab26af..46548bbf 100644
--- a/taskfile/task.go
+++ b/taskfile/task.go
@@ -20,6 +20,7 @@ type Task struct {
 	Env                  *Vars
 	Silent               bool
 	Interactive          bool
+	Internal             bool
 	Method               string
 	Prefix               string
 	IgnoreError          bool
@@ -64,6 +65,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
 		Env           *Vars
 		Silent        bool
 		Interactive   bool
+		Internal      bool
 		Method        string
 		Prefix        string
 		IgnoreError   bool `yaml:"ignore_error"`
@@ -86,6 +88,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	t.Env = task.Env
 	t.Silent = task.Silent
 	t.Interactive = task.Interactive
+	t.Internal = task.Internal
 	t.Method = task.Method
 	t.Prefix = task.Prefix
 	t.IgnoreError = task.IgnoreError
diff --git a/testdata/includes_internal/Taskfile.yml b/testdata/includes_internal/Taskfile.yml
new file mode 100644
index 00000000..f6c32167
--- /dev/null
+++ b/testdata/includes_internal/Taskfile.yml
@@ -0,0 +1,15 @@
+version: '3'
+
+includes:
+  included:
+    taskfile: Taskfile2.yml
+    internal: true
+
+tasks:
+  task-1:
+    cmds:
+      - task: included:default
+
+  task-2:
+    deps:
+      - included:default
diff --git a/testdata/includes_internal/Taskfile2.yml b/testdata/includes_internal/Taskfile2.yml
new file mode 100644
index 00000000..61869afe
--- /dev/null
+++ b/testdata/includes_internal/Taskfile2.yml
@@ -0,0 +1,6 @@
+version: '3'
+
+tasks:
+  task-3:
+    cmds:
+      - echo "Hello, World!"
diff --git a/testdata/internal_task/Taskfile.yml b/testdata/internal_task/Taskfile.yml
new file mode 100644
index 00000000..992cdb70
--- /dev/null
+++ b/testdata/internal_task/Taskfile.yml
@@ -0,0 +1,15 @@
+version: '3'
+
+tasks:
+  task-1:
+    cmds:
+      - task: task-3
+
+  task-2:
+    deps:
+      - task-3
+
+  task-3:
+    internal: true
+    cmds:
+      - echo "Hello, World!"
diff --git a/variables.go b/variables.go
index 80a232a3..fd2124f0 100644
--- a/variables.go
+++ b/variables.go
@@ -57,6 +57,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
 		Env:                  nil,
 		Silent:               origTask.Silent,
 		Interactive:          origTask.Interactive,
+		Internal:             origTask.Internal,
 		Method:               r.Replace(origTask.Method),
 		Prefix:               r.Replace(origTask.Prefix),
 		IgnoreError:          origTask.IgnoreError,