diff --git a/source/dub/dub.d b/source/dub/dub.d
index 964059a..72d6df7 100644
--- a/source/dub/dub.d
+++ b/source/dub/dub.d
@@ -126,23 +126,11 @@
 		PackageSupplier[] m_packageSuppliers;
 		NativePath m_rootPath;
 		SpecialDirs m_dirs;
-		DubConfig m_config;
+		UserConfiguration m_config;
 		NativePath m_projectPath;
 		Project m_project;
 		NativePath m_overrideSearchPath;
 		string m_defaultCompiler;
-		string m_defaultArchitecture;
-		bool m_defaultLowMemory;
-		string[string] m_defaultEnvironments;
-		string[string] m_defaultBuildEnvironments;
-		string[string] m_defaultRunEnvironments;
-		string[string] m_defaultPreGenerateEnvironments;
-		string[string] m_defaultPostGenerateEnvironments;
-		string[string] m_defaultPreBuildEnvironments;
-		string[string] m_defaultPostBuildEnvironments;
-		string[string] m_defaultPreRunEnvironments;
-		string[string] m_defaultPostRunEnvironments;
-
 	}
 
 	/** The default placement location of fetched packages.
@@ -223,7 +211,7 @@
 
 		if (skip_registry < SkipPackageSuppliers.configured)
 		{
-			ps ~= m_config.registryURLs
+			ps ~= m_config.registryUrls
 				.map!(url => getRegistryPackageSupplier(url))
 				.array;
 		}
@@ -245,13 +233,13 @@
 		scope (exit) environment.remove("DUB_REGISTRY");
 		auto dub = new Dub(".", null, SkipPackageSuppliers.none);
 
-		dub.m_config = new DubConfig(Json(["skipRegistry": Json("none")]), null);
+		dub.m_config.skipRegistry = typeof(dub.m_config.skipRegistry)(SkipPackageSuppliers.none);
 		assert(dub.getPackageSuppliers(null).length == 1);
 
-		dub.m_config = new DubConfig(Json(["skipRegistry": Json("configured")]), null);
+		dub.m_config.skipRegistry = typeof(dub.m_config.skipRegistry)(SkipPackageSuppliers.configured);
 		assert(dub.getPackageSuppliers(null).length == 0);
 
-		dub.m_config = new DubConfig(Json(["skipRegistry": Json("standard")]), null);
+		dub.m_config.skipRegistry = typeof(dub.m_config.skipRegistry)(SkipPackageSuppliers.standard);
 		assert(dub.getPackageSuppliers(null).length == 0);
 
 		environment["DUB_REGISTRY"] = "http://example.com/";
@@ -273,36 +261,31 @@
 
 	private void init(NativePath root_path)
 	{
+		import configy.Read;
+
 		this.m_dirs = SpecialDirs.make();
 
-		m_config = new DubConfig(jsonFromFile(m_dirs.systemSettings ~ "settings.json", true), m_config);
-
-		auto dubFolderPath = NativePath(thisExePath).parentPath;
-		m_config = new DubConfig(jsonFromFile(dubFolderPath ~ "../etc/dub/settings.json", true), m_config);
-		version (Posix) {
-			if (dubFolderPath.absolute && dubFolderPath.startsWith(NativePath("usr"))) {
-				m_config = new DubConfig(jsonFromFile(NativePath("/etc/dub/settings.json"), true), m_config);
-			}
+		void readSettingsFile (NativePath path_)
+		{
+			const path = path_.toNativeString();
+			if (path.exists)
+				this.m_config = this.m_config.merge(
+					parseConfigFile!UserConfiguration(CLIArgs(path)));
 		}
 
-		m_config = new DubConfig(jsonFromFile(m_dirs.userSettings ~ "settings.json", true), m_config);
+		const dubFolderPath = NativePath(thisExePath).parentPath;
 
+		readSettingsFile(m_dirs.systemSettings ~ "settings.json");
+		readSettingsFile(dubFolderPath ~ "../etc/dub/settings.json");
+		version (Posix) {
+			if (dubFolderPath.absolute && dubFolderPath.startsWith(NativePath("usr")))
+				readSettingsFile(NativePath("/etc/dub/settings.json"));
+		}
+		readSettingsFile(m_dirs.userSettings ~ "settings.json");
 		if (!root_path.empty)
-			m_config = new DubConfig(jsonFromFile(root_path ~ "dub.settings.json", true), m_config);
+			readSettingsFile(root_path ~ "dub.settings.json");
 
 		determineDefaultCompiler();
-
-		m_defaultArchitecture = m_config.defaultArchitecture;
-		m_defaultLowMemory = m_config.defaultLowMemory;
-		m_defaultEnvironments = m_config.defaultEnvironments;
-		m_defaultBuildEnvironments = m_config.defaultBuildEnvironments;
-		m_defaultRunEnvironments = m_config.defaultRunEnvironments;
-		m_defaultPreGenerateEnvironments = m_config.defaultPreGenerateEnvironments;
-		m_defaultPostGenerateEnvironments = m_config.defaultPostGenerateEnvironments;
-		m_defaultPreBuildEnvironments = m_config.defaultPreBuildEnvironments;
-		m_defaultPostBuildEnvironments = m_config.defaultPostBuildEnvironments;
-		m_defaultPreRunEnvironments = m_config.defaultPreRunEnvironments;
-		m_defaultPostRunEnvironments = m_config.defaultPostRunEnvironments;
 	}
 
 	@property bool dryRun() const { return m_dryRun; }
@@ -348,24 +331,24 @@
 		If set, the "defaultArchitecture" field of the DUB user or system
 		configuration file will be used. Otherwise null will be returned.
 	*/
-	@property string defaultArchitecture() const { return m_defaultArchitecture; }
+	@property string defaultArchitecture() const { return this.m_config.defaultArchitecture; }
 
 	/** Returns the default low memory option to use for building D code.
 
 		If set, the "defaultLowMemory" field of the DUB user or system
 		configuration file will be used. Otherwise false will be returned.
 	*/
-	@property bool defaultLowMemory() const { return m_defaultLowMemory; }
+	@property bool defaultLowMemory() const { return this.m_config.defaultLowMemory; }
 
-	@property const(string[string]) defaultEnvironments() const { return m_defaultEnvironments; }
-	@property const(string[string]) defaultBuildEnvironments() const { return m_defaultBuildEnvironments; }
-	@property const(string[string]) defaultRunEnvironments() const { return m_defaultRunEnvironments; }
-	@property const(string[string]) defaultPreGenerateEnvironments() const { return m_defaultPreGenerateEnvironments; }
-	@property const(string[string]) defaultPostGenerateEnvironments() const { return m_defaultPostGenerateEnvironments; }
-	@property const(string[string]) defaultPreBuildEnvironments() const { return m_defaultPreBuildEnvironments; }
-	@property const(string[string]) defaultPostBuildEnvironments() const { return m_defaultPostBuildEnvironments; }
-	@property const(string[string]) defaultPreRunEnvironments() const { return m_defaultPreRunEnvironments; }
-	@property const(string[string]) defaultPostRunEnvironments() const { return m_defaultPostRunEnvironments; }
+	@property const(string[string]) defaultEnvironments() const { return this.m_config.defaultEnvironments; }
+	@property const(string[string]) defaultBuildEnvironments() const { return this.m_config.defaultBuildEnvironments; }
+	@property const(string[string]) defaultRunEnvironments() const { return this.m_config.defaultRunEnvironments; }
+	@property const(string[string]) defaultPreGenerateEnvironments() const { return this.m_config.defaultPreGenerateEnvironments; }
+	@property const(string[string]) defaultPostGenerateEnvironments() const { return this.m_config.defaultPostGenerateEnvironments; }
+	@property const(string[string]) defaultPreBuildEnvironments() const { return this.m_config.defaultPreBuildEnvironments; }
+	@property const(string[string]) defaultPostBuildEnvironments() const { return this.m_config.defaultPostBuildEnvironments; }
+	@property const(string[string]) defaultPreRunEnvironments() const { return this.m_config.defaultPreRunEnvironments; }
+	@property const(string[string]) defaultPostRunEnvironments() const { return this.m_config.defaultPostRunEnvironments; }
 
 	/** Loads the package that resides within the configured `rootPath`.
 	*/
@@ -1260,17 +1243,28 @@
 		settings.config = "application";
 		settings.buildType = "debug";
 		settings.compiler = getCompiler(compiler_binary);
-		settings.platform = settings.compiler.determinePlatform(settings.buildSettings, compiler_binary, m_defaultArchitecture);
-		if (m_defaultLowMemory) settings.buildSettings.options |= BuildOption.lowmem;
-		if (m_defaultEnvironments) settings.buildSettings.addEnvironments(m_defaultEnvironments);
-		if (m_defaultBuildEnvironments) settings.buildSettings.addBuildEnvironments(m_defaultBuildEnvironments);
-		if (m_defaultRunEnvironments) settings.buildSettings.addRunEnvironments(m_defaultRunEnvironments);
-		if (m_defaultPreGenerateEnvironments) settings.buildSettings.addPreGenerateEnvironments(m_defaultPreGenerateEnvironments);
-		if (m_defaultPostGenerateEnvironments) settings.buildSettings.addPostGenerateEnvironments(m_defaultPostGenerateEnvironments);
-		if (m_defaultPreBuildEnvironments) settings.buildSettings.addPreBuildEnvironments(m_defaultPreBuildEnvironments);
-		if (m_defaultPostBuildEnvironments) settings.buildSettings.addPostBuildEnvironments(m_defaultPostBuildEnvironments);
-		if (m_defaultPreRunEnvironments) settings.buildSettings.addPreRunEnvironments(m_defaultPreRunEnvironments);
-		if (m_defaultPostRunEnvironments) settings.buildSettings.addPostRunEnvironments(m_defaultPostRunEnvironments);
+		settings.platform = settings.compiler.determinePlatform(
+			settings.buildSettings, compiler_binary, this.defaultArchitecture);
+		if (this.defaultLowMemory)
+			settings.buildSettings.options |= BuildOption.lowmem;
+		if (this.defaultEnvironments)
+			settings.buildSettings.addEnvironments(this.defaultEnvironments);
+		if (this.defaultBuildEnvironments)
+			settings.buildSettings.addBuildEnvironments(this.defaultBuildEnvironments);
+		if (this.defaultRunEnvironments)
+			settings.buildSettings.addRunEnvironments(this.defaultRunEnvironments);
+		if (this.defaultPreGenerateEnvironments)
+			settings.buildSettings.addPreGenerateEnvironments(this.defaultPreGenerateEnvironments);
+		if (this.defaultPostGenerateEnvironments)
+			settings.buildSettings.addPostGenerateEnvironments(this.defaultPostGenerateEnvironments);
+		if (this.defaultPreBuildEnvironments)
+			settings.buildSettings.addPreBuildEnvironments(this.defaultPreBuildEnvironments);
+		if (this.defaultPostBuildEnvironments)
+			settings.buildSettings.addPostBuildEnvironments(this.defaultPostBuildEnvironments);
+		if (this.defaultPreRunEnvironments)
+			settings.buildSettings.addPreRunEnvironments(this.defaultPreRunEnvironments);
+		if (this.defaultPostRunEnvironments)
+			settings.buildSettings.addPostRunEnvironments(this.defaultPostRunEnvironments);
 		settings.run = true;
 
 		return settings;
@@ -1778,146 +1772,156 @@
 	}
 }
 
-private class DubConfig {
-	private {
-		DubConfig m_parentConfig;
-		Json m_data;
-	}
+/**
+ * User-provided configuration
+ *
+ * All fields in this struct should be optional.
+ * Fields that are *not* optional should be mandatory from the POV
+ * of the application, not the POV of file parsing.
+ * For example, git's `core.author` and `core.email` are required to commit,
+ * but the error happens on the commit, not when the gitconfig is parsed.
+ *
+ * We have multiple configuration locations, and two kinds of fields:
+ * additive and non-additive. Additive fields are fields which are the union
+ * of all configuration files (e.g. `registryURLs`). Non-additive fields
+ * will ignore values set in lower priorities configuration, although parsing
+ * must still succeed. Additive fields are marked as `@Optional`,
+ * non-additive are marked as `SetInfo`.
+ */
+private struct UserConfiguration {
+	import configy.Attributes;
 
-	this(Json data, DubConfig parent_config)
+	@Optional string[] registryUrls;
+	@Optional NativePath[] customCachePaths;
+
+	SetInfo!(SkipPackageSuppliers) skipRegistry;
+	SetInfo!(string) defaultCompiler;
+	SetInfo!(string) defaultArchitecture;
+	SetInfo!(bool) defaultLowMemory;
+
+	SetInfo!(string[string]) defaultEnvironments;
+	SetInfo!(string[string]) defaultBuildEnvironments;
+	SetInfo!(string[string]) defaultRunEnvironments;
+	SetInfo!(string[string]) defaultPreGenerateEnvironments;
+	SetInfo!(string[string]) defaultPostGenerateEnvironments;
+	SetInfo!(string[string]) defaultPreBuildEnvironments;
+	SetInfo!(string[string]) defaultPostBuildEnvironments;
+	SetInfo!(string[string]) defaultPreRunEnvironments;
+	SetInfo!(string[string]) defaultPostRunEnvironments;
+
+	/// Merge a lower priority config (`this`) with a `higher` priority config
+	public UserConfiguration merge(UserConfiguration higher)
+		return @safe pure nothrow
 	{
-		m_data = data;
-		m_parentConfig = parent_config;
+		import std.traits : hasUDA;
+		UserConfiguration result;
+
+		static foreach (idx, _; UserConfiguration.tupleof) {
+			static if (hasUDA!(UserConfiguration.tupleof[idx], Optional))
+				result.tupleof[idx] = higher.tupleof[idx] ~ this.tupleof[idx];
+			else static if (IsSetInfo!(typeof(this.tupleof[idx]))) {
+				if (higher.tupleof[idx].set)
+					result.tupleof[idx] = higher.tupleof[idx];
+				else
+					result.tupleof[idx] = this.tupleof[idx];
+			} else
+				static assert(false,
+							  "Expect `@Optional` or `SetInfo` on: `" ~
+							  __traits(identifier, this.tupleof[idx]) ~
+							  "` of type : `" ~
+							  typeof(this.tupleof[idx]).stringof ~ "`");
+		}
+
+		return result;
 	}
 
-	@property string[] registryURLs()
-	{
-		string[] ret;
-		if (auto pv = "registryUrls" in m_data)
-			ret = (*pv).deserializeJson!(string[]);
-		if (m_parentConfig) ret ~= m_parentConfig.registryURLs;
-		return ret;
-	}
+	/// Workaround multiple `E` declaration in `static foreach` when inline
+	private template IsSetInfo(T) { enum bool IsSetInfo = is(T : SetInfo!E, E); }
+}
 
-	@property SkipPackageSuppliers skipRegistry()
-	{
-		if(auto pv = "skipRegistry" in m_data)
-			return to!SkipPackageSuppliers((*pv).get!string);
+unittest {
+	import configy.Read;
 
-		if (m_parentConfig)
-			return m_parentConfig.skipRegistry;
+    const str1 = `{
+  "registryUrls": [ "http://foo.bar" ],
+  "customCachePaths": [ "foo/bar", "foo/foo" ],
 
-		return SkipPackageSuppliers.none;
-	}
+  "skipRegistry": "all",
+  "defaultCompiler": "dmd",
+  "defaultArchitecture": "fooarch",
+  "defaultLowMemory": false,
 
-	@property NativePath[] customCachePaths()
-	{
-		import std.algorithm.iteration : map;
-		import std.array : array;
+  "defaultEnvironments": {
+    "VAR2": "settings.VAR2",
+    "VAR3": "settings.VAR3",
+    "VAR4": "settings.VAR4"
+  }
+}`;
 
-		NativePath[] ret;
-		if (auto pv = "customCachePaths" in m_data)
-			ret = (*pv).deserializeJson!(string[])
-				.map!(s => NativePath(s))
-				.array;
-		if (m_parentConfig)
-			ret ~= m_parentConfig.customCachePaths;
-		return ret;
-	}
+	const str2 = `{
+  "registryUrls": [ "http://bar.foo" ],
+  "customCachePaths": [ "bar/foo", "bar/bar" ],
 
-	@property string defaultCompiler()
-	const {
-		if (auto pv = "defaultCompiler" in m_data)
-			return pv.get!string;
-		if (m_parentConfig) return m_parentConfig.defaultCompiler;
-		return null;
-	}
+  "skipRegistry": "none",
+  "defaultCompiler": "ldc",
+  "defaultArchitecture": "bararch",
+  "defaultLowMemory": true,
 
-	@property string defaultArchitecture()
-	const {
-		if(auto pv = "defaultArchitecture" in m_data)
-			return (*pv).get!string;
-		if (m_parentConfig) return m_parentConfig.defaultArchitecture;
-		return null;
-	}
+  "defaultEnvironments": {
+    "VAR": "Hi",
+  }
+}`;
 
-	@property bool defaultLowMemory()
-	const {
-		if(auto pv = "defaultLowMemory" in m_data)
-			return (*pv).get!bool;
-		if (m_parentConfig) return m_parentConfig.defaultLowMemory;
-		return false;
-	}
+	 auto c1 = parseConfigString!UserConfiguration(str1, "/dev/null");
+	 assert(c1.registryUrls == [ "http://foo.bar" ]);
+	 assert(c1.customCachePaths == [ NativePath("foo/bar"), NativePath("foo/foo") ]);
+	 assert(c1.skipRegistry == SkipPackageSuppliers.all);
+	 assert(c1.defaultCompiler == "dmd");
+	 assert(c1.defaultArchitecture == "fooarch");
+	 assert(c1.defaultLowMemory == false);
+	 assert(c1.defaultEnvironments.length == 3);
+	 assert(c1.defaultEnvironments["VAR2"] == "settings.VAR2");
+	 assert(c1.defaultEnvironments["VAR3"] == "settings.VAR3");
+	 assert(c1.defaultEnvironments["VAR4"] == "settings.VAR4");
 
-	@property string[string] defaultEnvironments()
-	const {
-		if (auto pv = "defaultEnvironments" in m_data)
-			return deserializeJson!(string[string])(*cast(Json*)pv);
-		if (m_parentConfig) return m_parentConfig.defaultEnvironments;
-		return null;
-	}
+	 auto c2 = parseConfigString!UserConfiguration(str2, "/dev/null");
+	 assert(c2.registryUrls == [ "http://bar.foo" ]);
+	 assert(c2.customCachePaths == [ NativePath("bar/foo"), NativePath("bar/bar") ]);
+	 assert(c2.skipRegistry == SkipPackageSuppliers.none);
+	 assert(c2.defaultCompiler == "ldc");
+	 assert(c2.defaultArchitecture == "bararch");
+	 assert(c2.defaultLowMemory == true);
+	 assert(c2.defaultEnvironments.length == 1);
+	 assert(c2.defaultEnvironments["VAR"] == "Hi");
 
-	@property string[string] defaultBuildEnvironments()
-	const {
-		if (auto pv = "defaultBuildEnvironments" in m_data)
-			return deserializeJson!(string[string])(*cast(Json*)pv);
-		if (m_parentConfig) return m_parentConfig.defaultBuildEnvironments;
-		return null;
-	}
+	 auto m1 = c2.merge(c1);
+	 // c1 takes priority, so its registryUrls is first
+	 assert(m1.registryUrls == [ "http://foo.bar", "http://bar.foo" ]);
+	 // Same with CCP
+	 assert(m1.customCachePaths == [
+		 NativePath("foo/bar"), NativePath("foo/foo"),
+		 NativePath("bar/foo"), NativePath("bar/bar"),
+	 ]);
 
-	@property string[string] defaultRunEnvironments()
-	const {
-		if (auto pv = "defaultRunEnvironments" in m_data)
-			return deserializeJson!(string[string])(*cast(Json*)pv);
-		if (m_parentConfig) return m_parentConfig.defaultRunEnvironments;
-		return null;
-	}
+	 // c1 fields only
+	 assert(m1.skipRegistry == c1.skipRegistry);
+	 assert(m1.defaultCompiler == c1.defaultCompiler);
+	 assert(m1.defaultArchitecture == c1.defaultArchitecture);
+	 assert(m1.defaultLowMemory == c1.defaultLowMemory);
+	 assert(m1.defaultEnvironments == c1.defaultEnvironments);
 
-	@property string[string] defaultPreGenerateEnvironments()
-	const {
-		if (auto pv = "defaultPreGenerateEnvironments" in m_data)
-			return deserializeJson!(string[string])(*cast(Json*)pv);
-		if (m_parentConfig) return m_parentConfig.defaultPreGenerateEnvironments;
-		return null;
-	}
+	 auto m2 = c1.merge(c2);
+	 assert(m2.registryUrls == [ "http://bar.foo", "http://foo.bar" ]);
+	 assert(m2.customCachePaths == [
+		 NativePath("bar/foo"), NativePath("bar/bar"),
+		 NativePath("foo/bar"), NativePath("foo/foo"),
+	 ]);
+	 assert(m2.skipRegistry == c2.skipRegistry);
+	 assert(m2.defaultCompiler == c2.defaultCompiler);
+	 assert(m2.defaultArchitecture == c2.defaultArchitecture);
+	 assert(m2.defaultLowMemory == c2.defaultLowMemory);
+	 assert(m2.defaultEnvironments == c2.defaultEnvironments);
 
-	@property string[string] defaultPostGenerateEnvironments()
-	const {
-		if (auto pv = "defaultPostGenerateEnvironments" in m_data)
-			return deserializeJson!(string[string])(*cast(Json*)pv);
-		if (m_parentConfig) return m_parentConfig.defaultPostGenerateEnvironments;
-		return null;
-	}
-
-	@property string[string] defaultPreBuildEnvironments()
-	const {
-		if (auto pv = "defaultPreBuildEnvironments" in m_data)
-			return deserializeJson!(string[string])(*cast(Json*)pv);
-		if (m_parentConfig) return m_parentConfig.defaultPreBuildEnvironments;
-		return null;
-	}
-
-	@property string[string] defaultPostBuildEnvironments()
-	const {
-		if (auto pv = "defaultPostBuildEnvironments" in m_data)
-			return deserializeJson!(string[string])(*cast(Json*)pv);
-		if (m_parentConfig) return m_parentConfig.defaultPostBuildEnvironments;
-		return null;
-	}
-
-	@property string[string] defaultPreRunEnvironments()
-	const {
-		if (auto pv = "defaultPreRunEnvironments" in m_data)
-			return deserializeJson!(string[string])(*cast(Json*)pv);
-		if (m_parentConfig) return m_parentConfig.defaultPreRunEnvironments;
-		return null;
-	}
-
-	@property string[string] defaultPostRunEnvironments()
-	const {
-		if (auto pv = "defaultPostRunEnvironments" in m_data)
-			return deserializeJson!(string[string])(*cast(Json*)pv);
-		if (m_parentConfig) return m_parentConfig.defaultPostRunEnvironments;
-		return null;
-	}
+	 auto m3 = UserConfiguration.init.merge(c1);
+	 assert(m3 == c1);
 }