diff --git a/lib/mix/lib/mix.ex b/lib/mix/lib/mix.ex
index 55efee375a9..46785e60220 100644
--- a/lib/mix/lib/mix.ex
+++ b/lib/mix/lib/mix.ex
@@ -359,7 +359,11 @@ defmodule Mix do
     * `MIX_OS_CONCURRENCY_LOCK` - when set to `0` or `false`, disables mix compilation locking.
       While not recommended, this may be necessary in cases where hard links or TCP sockets are
       not available. When opting for this behaviour, make sure to not start concurrent compilations
-      of the same project.
+      of the same project
+
+    * `MIX_OS_DEPS_COMPILE_PARTITION_COUNT` - when set to a number greater than 1, it enables
+      compilation of dependencies over multiple operating system processes. See `mix help deps.compile`
+      for more information
 
     * `MIX_PATH` - appends extra code paths
 
@@ -420,7 +424,7 @@ defmodule Mix do
 
   """
 
-  @mix_install_project __MODULE__.InstallProject
+  @mix_install_project Mix.InstallProject
   @mix_install_app :mix_install
   @mix_install_app_string Atom.to_string(@mix_install_app)
 
@@ -905,9 +909,7 @@ defmodule Mix do
 
     case Mix.State.get(:installed) do
       nil ->
-        Application.put_all_env(config, persistent: true)
         System.put_env(system_env)
-
         install_project_dir = install_project_dir(id)
 
         if Keyword.fetch!(opts, :verbose) do
@@ -924,10 +926,14 @@ defmodule Mix do
           config_path: config_path
         ]
 
-        config = install_project_config(dynamic_config)
+        :ok =
+          Mix.ProjectStack.push(
+            @mix_install_project,
+            [compile_config: config] ++ install_project_config(dynamic_config),
+            "nofile"
+          )
 
         started_apps = Application.started_applications()
-        :ok = Mix.ProjectStack.push(@mix_install_project, config, "nofile")
         build_dir = Path.join(install_project_dir, "_build")
         external_lockfile = expand_path(opts[:lockfile], deps, :lockfile, "mix.lock")
 
@@ -944,9 +950,9 @@ defmodule Mix do
           File.mkdir_p!(install_project_dir)
 
           File.cd!(install_project_dir, fn ->
-            if config_path do
-              Mix.Task.rerun("loadconfig")
-            end
+            # This steps need to be mirror in mix deps.partition
+            Application.put_all_env(config, persistent: true)
+            Mix.Task.rerun("loadconfig")
 
             cond do
               external_lockfile ->
@@ -1079,7 +1085,7 @@ defmodule Mix do
 
   defp install_project_config(dynamic_config) do
     [
-      version: "0.1.0",
+      version: "1.0.0",
       build_per_environment: true,
       build_path: "_build",
       lockfile: "mix.lock",
diff --git a/lib/mix/lib/mix/tasks/deps.compile.ex b/lib/mix/lib/mix/tasks/deps.compile.ex
index 7ce84b4da32..962b7be9f3f 100644
--- a/lib/mix/lib/mix/tasks/deps.compile.ex
+++ b/lib/mix/lib/mix/tasks/deps.compile.ex
@@ -35,6 +35,24 @@ defmodule Mix.Tasks.Deps.Compile do
   recompiled without propagating those changes upstream. To ensure
   `b` is included in the compilation step, pass `--include-children`.
 
+  ## Compiling dependencies across multiple OS processes
+
+  If you set the environment variable `MIX_OS_DEPS_COMPILE_PARTITION_COUNT`
+  to a number greater than 1, Mix will start multiple operating system
+  processes to compile your dependencies concurrently.
+
+  While Mix and Rebar compile all files within a given project in parallel,
+  enabling this environment variable can still yield useful gains in several
+  cases, such as when compiling dependencies with native code, dependencies
+  that must download assets, or dependencies where the compilation time is not
+  evenly distributed (for example, one file takes much longer to compile than
+  all others).
+
+  While most configuration in Mix is done via command line flags, this particular
+  environment variable exists because the best number will vary per machine
+  (and often per project too). The environment variable also makes it more accessible
+  to enable concurrent compilation in CI and also during `Mix.install/2` commands.
+
   ## Command line options
 
     * `--force` - force compilation of deps
@@ -57,7 +75,6 @@ defmodule Mix.Tasks.Deps.Compile do
     end
 
     Mix.Project.get!()
-
     config = Mix.Project.config()
 
     Mix.Project.with_build_lock(config, fn ->
@@ -75,86 +92,82 @@ defmodule Mix.Tasks.Deps.Compile do
 
   @doc false
   def compile(deps, options \\ []) do
-    shell = Mix.shell()
-    config = Mix.Project.deps_config()
     Mix.Task.run("deps.precompile")
+    force? = Keyword.get(options, :force, false)
 
-    compiled =
+    deps =
       deps
       |> reject_umbrella_children(options)
       |> reject_local_deps(options)
-      |> Enum.map(fn %Mix.Dep{app: app, status: status, opts: opts, scm: scm} = dep ->
-        check_unavailable!(app, scm, status)
-        maybe_clean(dep, options)
 
-        compiled? =
-          cond do
-            not is_nil(opts[:compile]) ->
-              do_compile(dep, config)
+    count = System.get_env("MIX_OS_DEPS_COMPILE_PARTITION_COUNT", "0") |> String.to_integer()
 
-            Mix.Dep.mix?(dep) ->
-              do_mix(dep, config)
+    compiled? =
+      if count > 1 and length(deps) > 1 do
+        Mix.shell().info("mix deps.compile running across #{count} OS processes")
+        Mix.Tasks.Deps.Partition.server(deps, count, force?)
+      else
+        config = Mix.Project.deps_config()
+        true in Enum.map(deps, &compile_single(&1, force?, config))
+      end
 
-            Mix.Dep.make?(dep) ->
-              do_make(dep, config)
+    if compiled?, do: Mix.Task.run("will_recompile"), else: :ok
+  end
 
-            dep.manager == :rebar3 ->
-              do_rebar3(dep, config)
+  @doc false
+  def compile_single(%Mix.Dep{} = dep, force?, config) do
+    %{app: app, status: status, opts: opts, scm: scm} = dep
+    check_unavailable!(app, scm, status)
 
-            true ->
-              shell.error(
-                "Could not compile #{inspect(app)}, no \"mix.exs\", \"rebar.config\" or \"Makefile\" " <>
-                  "(pass :compile as an option to customize compilation, set it to \"false\" to do nothing)"
-              )
+    # If a dependency was marked as fetched or with an out of date lock
+    # or missing the app file, we always compile it from scratch.
+    if force? or Mix.Dep.compilable?(dep) do
+      File.rm_rf!(Path.join([Mix.Project.build_path(), "lib", Atom.to_string(dep.app)]))
+    end
 
-              false
-          end
+    compiled? =
+      cond do
+        not is_nil(opts[:compile]) ->
+          do_compile(dep, config)
 
-        if compiled? do
-          build_path = Mix.Project.build_path(config)
+        Mix.Dep.mix?(dep) ->
+          do_mix(dep, config)
 
-          lazy_message = fn ->
-            info = %{
-              app: dep.app,
-              scm: dep.scm,
-              manager: dep.manager,
-              os_pid: System.pid()
-            }
+        Mix.Dep.make?(dep) ->
+          do_make(dep, config)
 
-            {:dep_compiled, info}
-          end
+        dep.manager == :rebar3 ->
+          do_rebar3(dep, config)
 
-          Mix.Sync.PubSub.broadcast(build_path, lazy_message)
-        end
+        true ->
+          Mix.shell().error(
+            "Could not compile #{inspect(app)}, no \"mix.exs\", \"rebar.config\" or \"Makefile\" " <>
+              "(pass :compile as an option to customize compilation, set it to \"false\" to do nothing)"
+          )
 
-        # We should touch fetchable dependencies even if they
-        # did not compile otherwise they will always be marked
-        # as stale, even when there is nothing to do.
-        fetchable? = touch_fetchable(scm, opts[:build])
+          false
+      end
 
-        compiled? and fetchable?
+    if compiled? do
+      config
+      |> Mix.Project.build_path()
+      |> Mix.Sync.PubSub.broadcast(fn ->
+        info = %{
+          app: dep.app,
+          scm: dep.scm,
+          manager: dep.manager,
+          os_pid: System.pid()
+        }
+
+        {:dep_compiled, info}
       end)
-
-    if true in compiled, do: Mix.Task.run("will_recompile"), else: :ok
-  end
-
-  defp maybe_clean(dep, opts) do
-    # If a dependency was marked as fetched or with an out of date lock
-    # or missing the app file, we always compile it from scratch.
-    if Keyword.get(opts, :force, false) or Mix.Dep.compilable?(dep) do
-      File.rm_rf!(Path.join([Mix.Project.build_path(), "lib", Atom.to_string(dep.app)]))
     end
-  end
 
-  defp touch_fetchable(scm, path) do
-    if scm.fetchable?() do
-      path = Path.join(path, ".mix")
-      File.mkdir_p!(path)
-      File.touch!(Path.join(path, "compile.fetch"))
-      true
-    else
-      false
-    end
+    # We should touch fetchable dependencies even if they
+    # did not compile otherwise they will always be marked
+    # as stale, even when there is nothing to do.
+    fetchable? = touch_fetchable(scm, opts[:build])
+    compiled? and fetchable?
   end
 
   defp check_unavailable!(app, scm, {:unavailable, path}) do
@@ -176,6 +189,17 @@ defmodule Mix.Tasks.Deps.Compile do
     :ok
   end
 
+  defp touch_fetchable(scm, path) do
+    if scm.fetchable?() do
+      path = Path.join(path, ".mix")
+      File.mkdir_p!(path)
+      File.touch!(Path.join(path, "compile.fetch"))
+      true
+    else
+      false
+    end
+  end
+
   defp do_mix(dep, _config) do
     Mix.Dep.in_dependency(dep, fn _ ->
       config = Mix.Project.config()
diff --git a/lib/mix/lib/mix/tasks/deps.loadpaths.ex b/lib/mix/lib/mix/tasks/deps.loadpaths.ex
index 30c6fa2d49f..ccd084e5bea 100644
--- a/lib/mix/lib/mix/tasks/deps.loadpaths.ex
+++ b/lib/mix/lib/mix/tasks/deps.loadpaths.ex
@@ -139,11 +139,7 @@ defmodule Mix.Tasks.Deps.Loadpaths do
   defp partition([dep | deps], not_ok, compile) do
     cond do
       Mix.Dep.compilable?(dep) or (Mix.Dep.ok?(dep) and local?(dep)) ->
-        if from_umbrella?(dep) do
-          partition(deps, not_ok, compile)
-        else
-          partition(deps, not_ok, [dep | compile])
-        end
+        partition(deps, not_ok, [dep | compile])
 
       Mix.Dep.ok?(dep) ->
         partition(deps, not_ok, compile)
@@ -163,11 +159,6 @@ defmodule Mix.Tasks.Deps.Loadpaths do
     |> Mix.Dep.filter_by_name(Mix.Dep.load_and_cache())
   end
 
-  # Those are compiled by umbrella.
-  defp from_umbrella?(dep) do
-    dep.opts[:from_umbrella]
-  end
-
   # Every local dependency (i.e. that are not fetchable)
   # are automatically recompiled if they are ok.
   defp local?(dep) do
diff --git a/lib/mix/lib/mix/tasks/deps.partition.ex b/lib/mix/lib/mix/tasks/deps.partition.ex
new file mode 100644
index 00000000000..301a6443710
--- /dev/null
+++ b/lib/mix/lib/mix/tasks/deps.partition.ex
@@ -0,0 +1,261 @@
+# SPDX-License-Identifier: Apache-2.0
+# SPDX-FileCopyrightText: 2021 The Elixir Team
+
+defmodule Mix.Tasks.Deps.Partition do
+  @moduledoc false
+  use Mix.Task
+
+  ## Server
+
+  @deps_partition_install_mix_exs ~c"deps.partition.mix.exs"
+
+  def server(deps, count, force?) do
+    {:ok, socket} = :gen_tcp.listen(0, [:binary, packet: :line, active: false])
+
+    try do
+      server(socket, deps, count, force?)
+    after
+      :gen_tcp.close(socket)
+    end
+  end
+
+  defp server(socket, deps, count, force?) do
+    elixir =
+      System.get_env("MIX_OS_DEPS_COMPILE_PARTITION_ELIXIR_EXECUTABLE") ||
+        System.find_executable("elixir") ||
+        raise "cannot find elixir executable for partition compilation"
+
+    {:ok, {_ip, port}} = :inet.sockname(socket)
+    ansi_flag = if IO.ANSI.enabled?(), do: ~c"--color", else: ~c"--no-color"
+    force_flag = if force?, do: ~c"--force", else: ~c"--no-force"
+
+    env_vars =
+      if Mix.install?() do
+        blob =
+          Mix.Project.config()
+          |> :erlang.term_to_binary()
+          |> :binary.bin_to_list()
+          |> Enum.join(",")
+
+        # We replicate the initialization logic from Mix.install/2 as part of mix.exs
+        File.write!(@deps_partition_install_mix_exs, """
+        config = <<#{blob}>>
+        project = :erlang.binary_to_term(config)
+
+        if compile_config = project[:compile_config] do
+          Application.put_all_env(compile_config, persistent: true)
+        end
+
+        Mix.ProjectStack.push(Mix.InstallProject, project, "nofile")
+        """)
+
+        [{~c"MIX_EXS", @deps_partition_install_mix_exs}]
+      else
+        []
+      end
+
+    args = [
+      ansi_flag,
+      ~c"-e",
+      ~c"Mix.CLI.main",
+      ~c"deps.partition",
+      force_flag,
+      ~c"--port",
+      Integer.to_charlist(port),
+      ~c"--host",
+      ~c"127.0.0.1"
+    ]
+
+    options = [
+      :binary,
+      :hide,
+      :use_stdio,
+      :stderr_to_stdout,
+      line: 1_000_000,
+      env: [{~c"MIX_OS_CONCURRENCY_LOCK", ~c"false"} | env_vars]
+    ]
+
+    ports =
+      Map.new(1..count//1, fn index ->
+        if Mix.debug?() do
+          IO.puts("-> Starting mix deps.partition ##{index}")
+        end
+
+        args = args ++ [~c"--index", Integer.to_charlist(index)]
+        port = Port.open({:spawn_executable, String.to_charlist(elixir)}, [args: args] ++ options)
+
+        {index, port}
+      end)
+
+    clients =
+      Enum.map(1..count//1, fn _ ->
+        with {:ok, client} <- :gen_tcp.accept(socket, 15000),
+             {:ok, message} <- :gen_tcp.recv(client, 0, 15000) do
+          :inet.setopts(client, active: true)
+          index = message |> String.trim() |> String.to_integer()
+          %{port: Map.fetch!(ports, index), index: index, socket: client}
+        else
+          error ->
+            logs =
+              Enum.map_join(ports, "\n", fn {index, port} -> close_port(port, "#{index} >") end)
+
+            Mix.raise("""
+            could not start partition dependency compiler, no connection made to TCP port: #{inspect(error)}
+
+            #{logs}
+            """)
+        end
+      end)
+
+    status = Map.new(deps, &{&1.app, :pending})
+    send_deps_and_server_loop(clients, [], deps, status)
+  end
+
+  defp send_deps_and_server_loop(available, busy, deps, status) do
+    {available, busy, deps} = send_deps(available, busy, deps, status)
+    server_loop(available, busy, deps, status)
+  end
+
+  defp send_deps([client | available], busy, deps, status) do
+    case pop_with(deps, fn dep ->
+           Enum.all?(dep.deps, &(Map.get(status, &1.app, :unknown) != :pending))
+         end) do
+      :error ->
+        {[client | available], busy, deps}
+
+      {dep, deps} ->
+        if Mix.debug?() do
+          Mix.shell().info("-- Sending #{dep.app} to mix deps.partition #{client.index}")
+        end
+
+        :gen_tcp.send(client.socket, "#{dep.app}\n")
+        send_deps(available, [client | busy], deps, status)
+    end
+  end
+
+  defp send_deps([], busy, deps, _status) do
+    {[], busy, deps}
+  end
+
+  defp server_loop(available, _busy = [], _deps = [], status) do
+    shutdown_clients(available)
+    Enum.any?(status, &(elem(&1, 1) == true))
+  end
+
+  defp server_loop(available, busy, deps, status) do
+    receive do
+      {:tcp, socket, data} ->
+        [app, compiled?] =
+          data |> String.trim() |> String.split(":") |> Enum.map(&String.to_atom/1)
+
+        deps = Enum.reject(deps, &(&1.app == app))
+        status = Map.replace!(status, app, compiled?)
+        {client, busy} = pop_with(busy, &(&1.socket == socket))
+
+        if Mix.debug?() do
+          Mix.shell().info("-- mix deps.partition #{client.index} compiled #{app}")
+        end
+
+        send_deps_and_server_loop([client | available], busy, deps, status)
+
+      {:tcp_closed, socket} ->
+        shutdown_clients(available ++ busy)
+        Mix.raise("mix deps.partition #{inspect(socket)} closed unexpectedly")
+
+      {:tcp_error, socket, error} ->
+        shutdown_clients(available ++ busy)
+        Mix.raise("mix deps.partition #{inspect(socket)} errored: #{inspect(error)}")
+
+      {port, {:data, {eol, data}}} ->
+        with %{index: index} <-
+               Enum.find(busy, &(&1.port == port)) || Enum.find(available, &(&1.port == port)) do
+          terminator = if eol == :eol, do: "\n", else: ""
+          IO.write([Integer.to_string(index), "> ", data, terminator])
+        end
+
+        server_loop(available, busy, deps, status)
+    end
+  end
+
+  defp pop_with(list, fun) do
+    case Enum.split_while(list, &(not fun.(&1))) do
+      {_, []} -> :error
+      {pre, [result | post]} -> {result, pre ++ post}
+    end
+  end
+
+  defp shutdown_clients(clients) do
+    Enum.each(clients, fn %{socket: socket, port: port, index: index} ->
+      if Mix.debug?() do
+        IO.puts("-> Closing mix deps.partition ##{index}")
+      end
+
+      _ = :gen_tcp.close(socket)
+      IO.write(close_port(port, "#{index}> "))
+    end)
+  end
+
+  defp close_port(port, prefix) do
+    receive do
+      {^port, {:data, {:eol, data}}} -> [prefix, data, ?\n | close_port(port, prefix)]
+      {^port, {:data, {:noeol, data}}} -> [data | close_port(port, prefix)]
+    after
+      0 ->
+        try do
+          Port.close(port)
+        catch
+          _, _ -> :ok
+        end
+
+        []
+    end
+  end
+
+  ## Client
+
+  @switches [port: :integer, host: :string, force: :boolean, index: :string]
+
+  @impl true
+  def run(args) do
+    # If stdin closes, we shutdown the VM
+    spawn(fn ->
+      _ = IO.gets("")
+      System.halt(0)
+    end)
+
+    {opts, []} = OptionParser.parse!(args, strict: @switches)
+    host = Keyword.fetch!(opts, :host)
+    port = Keyword.fetch!(opts, :port)
+    index = Keyword.fetch!(opts, :index)
+    force? = Keyword.get(opts, :force, false)
+
+    {:ok, socket} =
+      :gen_tcp.connect(String.to_charlist(host), port, [:binary, packet: :line, active: false])
+
+    :gen_tcp.send(socket, "#{index}\n")
+
+    try do
+      deps = Mix.Dep.load_and_cache()
+      client_loop(socket, deps, force?, Mix.Project.deps_config())
+    after
+      :gen_tcp.close(socket)
+    end
+  end
+
+  def client_loop(socket, deps, force?, config) do
+    case :gen_tcp.recv(socket, 0, :infinity) do
+      {:ok, app} ->
+        app = app |> String.trim() |> String.to_atom()
+
+        dep =
+          Enum.find(deps, &(&1.app == app)) || raise "could not find dependency #{inspect(app)}"
+
+        compiled? = Mix.Tasks.Deps.Compile.compile_single(dep, force?, config)
+        :ok = :gen_tcp.send(socket, "#{app}:#{compiled?}\n")
+        client_loop(socket, deps, force?, config)
+
+      {:error, :closed} ->
+        :ok
+    end
+  end
+end
diff --git a/lib/mix/lib/mix/tasks/loadconfig.ex b/lib/mix/lib/mix/tasks/loadconfig.ex
index 61e058e5a10..22dff3606c2 100644
--- a/lib/mix/lib/mix/tasks/loadconfig.ex
+++ b/lib/mix/lib/mix/tasks/loadconfig.ex
@@ -40,10 +40,10 @@ defmodule Mix.Tasks.Loadconfig do
   end
 
   defp load_default do
-    config = Mix.Project.config()
+    config_path = Mix.Project.config()[:config_path]
 
-    if File.regular?(config[:config_path]) or config[:config_path] != "config/config.exs" do
-      load_compile(config[:config_path])
+    if config_path != nil and (File.regular?(config_path) or config_path != "config/config.exs") do
+      load_compile(config_path)
     else
       []
     end
diff --git a/lib/mix/test/fixtures/umbrella_dep/mix.exs b/lib/mix/test/fixtures/umbrella_dep/mix.exs
index 9fefc54fdde..99b72b6c7e0 100644
--- a/lib/mix/test/fixtures/umbrella_dep/mix.exs
+++ b/lib/mix/test/fixtures/umbrella_dep/mix.exs
@@ -4,6 +4,7 @@ defmodule UmbrellaDep.MixProject do
   def project do
     [
       app: :umbrella_dep,
+      version: "0.1.0",
       deps: deps()
     ]
   end
diff --git a/lib/mix/test/mix/tasks/deps_test.exs b/lib/mix/test/mix/tasks/deps_test.exs
index 7e9cf12a5e1..a1f04e386cf 100644
--- a/lib/mix/test/mix/tasks/deps_test.exs
+++ b/lib/mix/test/mix/tasks/deps_test.exs
@@ -61,7 +61,7 @@ defmodule Mix.Tasks.DepsTest do
     end
   end
 
-  defmodule RawRepoDep do
+  defmodule RawRepoDepApp do
     def project do
       [
         app: :raw_sample,
@@ -219,6 +219,38 @@ defmodule Mix.Tasks.DepsTest do
     end)
   end
 
+  test "compiles deps using os partitions" do
+    System.put_env("MIX_OS_DEPS_COMPILE_PARTITION_COUNT", "2")
+
+    in_fixture("deps_status", fn ->
+      File.write!("mix.exs", """
+      defmodule ParDepsApp do
+        use Mix.Project
+
+        def project do
+          [
+            app: :par_sample,
+            version: "0.1.0",
+            deps: [
+              {:raw_repo, "0.1.0", path: "custom/raw_repo"},
+              {:git_repo, "0.1.0", path: #{inspect(fixture_path("git_repo"))}}
+            ]
+          ]
+        end
+      end
+      """)
+
+      Mix.Project.in_project(:par_sample, ".", fn _ ->
+        output = ExUnit.CaptureIO.capture_io(fn -> Mix.Tasks.Deps.Compile.run([]) end)
+        assert output =~ ~r/\d> Generated git_repo app/
+        assert output =~ ~r/\d> Generated raw_repo app/
+        assert_received {:mix_shell, :info, ["mix deps.compile running across 2 OS processes"]}
+      end)
+    end)
+  after
+    System.delete_env("MIX_OS_DEPS_COMPILE_PARTITION_COUNT")
+  end
+
   test "doesn't compile any umbrella apps if --skip-umbrella-children is given" do
     in_fixture("umbrella_dep/deps/umbrella", fn ->
       Mix.Project.in_project(:umbrella, ".", fn _ ->
@@ -414,7 +446,7 @@ defmodule Mix.Tasks.DepsTest do
 
   test "sets deps env to prod by default" do
     in_fixture("deps_status", fn ->
-      Mix.Project.push(RawRepoDep)
+      Mix.Project.push(RawRepoDepApp)
 
       Mix.Tasks.Deps.Update.run(["--all"])
       assert_received {:mix_shell, :info, [":raw_repo env is prod"]}
@@ -751,7 +783,7 @@ defmodule Mix.Tasks.DepsTest do
 
   test "checks if compile env changed" do
     in_fixture("deps_status", fn ->
-      Mix.Project.push(RawRepoDep)
+      Mix.Project.push(RawRepoDepApp)
       Mix.Tasks.Deps.Loadpaths.run([])
       assert_receive {:mix_shell, :info, ["Generated raw_repo app"]}
       assert Application.spec(:raw_repo, :vsn)
@@ -766,7 +798,7 @@ defmodule Mix.Tasks.DepsTest do
       Application.unload(:raw_repo)
       Mix.ProjectStack.pop()
       Mix.Task.clear()
-      Mix.Project.push(RawRepoDep)
+      Mix.Project.push(RawRepoDepApp)
       purge([RawRepo])
       Mix.Tasks.Loadconfig.load_compile("config/config.exs")
 
diff --git a/lib/mix/test/mix/tasks/loadconfig_test.exs b/lib/mix/test/mix/tasks/loadconfig_test.exs
index 1b78e23190e..715bfc62238 100644
--- a/lib/mix/test/mix/tasks/loadconfig_test.exs
+++ b/lib/mix/test/mix/tasks/loadconfig_test.exs
@@ -67,6 +67,11 @@ defmodule Mix.Tasks.LoadconfigTest do
     end)
   end
 
+  test "is a no-op with nil custom config_path" do
+    Mix.ProjectStack.post_config(config_path: nil)
+    assert Mix.Task.run("loadconfig", []) == []
+  end
+
   test "updates config files and config mtime", context do
     in_tmp(context.test, fn ->
       Mix.Project.push(MixTest.Case.Sample)
diff --git a/lib/mix/test/mix/umbrella_test.exs b/lib/mix/test/mix/umbrella_test.exs
index 76ac93aec5a..37303777352 100644
--- a/lib/mix/test/mix/umbrella_test.exs
+++ b/lib/mix/test/mix/umbrella_test.exs
@@ -276,10 +276,27 @@ defmodule Mix.UmbrellaTest do
   test "compile for umbrella as dependency" do
     in_fixture("umbrella_dep", fn ->
       Mix.Project.in_project(:umbrella_dep, ".", fn _ ->
-        Mix.Task.run("deps.compile")
+        Mix.Task.run("compile")
+        assert Bar.bar() == "hello world"
+      end)
+    end)
+  end
+
+  test "compile for umbrella as dependency with os partitions" do
+    System.put_env("MIX_OS_DEPS_COMPILE_PARTITION_COUNT", "2")
+
+    in_fixture("umbrella_dep", fn ->
+      Mix.Project.in_project(:umbrella_dep, ".", fn _ ->
+        output = ExUnit.CaptureIO.capture_io(fn -> Mix.Task.run("compile") end)
+        assert output =~ ~r/\d> :foo env is prod/
+        assert output =~ ~r/\d> :bar env is prod/
+
+        assert_received {:mix_shell, :info, ["mix deps.compile running across 2 OS processes"]}
         assert Bar.bar() == "hello world"
       end)
     end)
+  after
+    System.delete_env("MIX_OS_DEPS_COMPILE_PARTITION_COUNT")
   end
 
   defmodule CycleDeps do
@@ -351,7 +368,7 @@ defmodule Mix.UmbrellaTest do
           def project do
             [app: :bar,
              version: "0.1.0",
-             aliases: ["compile.all": fn _ -> Mix.shell().info "no compile bar" end]]
+             aliases: ["compile.elixir": fn _ -> Mix.shell().info "no compile bar" end]]
           end
         end
         """)
diff --git a/lib/mix/test/test_helper.exs b/lib/mix/test/test_helper.exs
index 0d9a96addba..05f21bc2df9 100644
--- a/lib/mix/test/test_helper.exs
+++ b/lib/mix/test/test_helper.exs
@@ -10,9 +10,6 @@ mix = Path.expand("../tmp/.mix", __DIR__)
 File.mkdir_p!(mix)
 System.put_env("MIX_HOME", mix)
 
-System.delete_env("XDG_DATA_HOME")
-System.delete_env("XDG_CONFIG_HOME")
-
 # Load protocols to make sure they are not unloaded during tests
 [Collectable, Enumerable, Inspect, String.Chars, List.Chars]
 |> Enum.each(& &1.__protocol__(:module))
@@ -52,14 +49,6 @@ ExUnit.start(
   include: line_include
 )
 
-# Clear environment variables that may affect tests
-System.delete_env("http_proxy")
-System.delete_env("https_proxy")
-System.delete_env("HTTP_PROXY")
-System.delete_env("HTTPS_PROXY")
-System.delete_env("MIX_ENV")
-System.delete_env("MIX_TARGET")
-
 defmodule MixTest.Case do
   use ExUnit.CaseTemplate
 
@@ -232,11 +221,11 @@ defmodule MixTest.Case do
     File.write!(file, File.read!(file) <> "\n")
   end
 
-  defp mix_executable do
+  def mix_executable do
     Path.expand("../../../bin/mix", __DIR__)
   end
 
-  defp elixir_executable do
+  def elixir_executable do
     Path.expand("../../../bin/elixir", __DIR__)
   end
 
@@ -252,6 +241,20 @@ defmodule MixTest.Case do
   end
 end
 
+# Prepare and clear environment variables
+System.put_env(
+  "MIX_OS_DEPS_COMPILE_PARTITION_ELIXIR_EXECUTABLE",
+  MixTest.Case.elixir_executable()
+)
+
+# Clear environment variables that may affect tests
+Enum.each(
+  ~w(http_proxy https_proxy HTTP_PROXY HTTPS_PROXY) ++
+    ~w(MIX_ENV MIX_OS_DEPS_COMPILE_PARTITION_COUNT MIX_TARGET) ++
+    ~w(XDG_DATA_HOME XDG_CONFIG_HOME),
+  &System.delete_env/1
+)
+
 ## Set up Rebar fixtures
 
 rebar3_source = System.get_env("REBAR3") || Path.expand("fixtures/rebar3", __DIR__)