diff --git a/Makefile.am b/Makefile.am
index 68552f74a..182987d07 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -148,7 +148,8 @@ src_mpd_SOURCES = \
 	src/Idle.cxx src/Idle.hxx \
 	src/CommandLine.cxx src/CommandLine.hxx \
 	src/ConfigFile.cxx \
-	src/ConfigTemplates.hxx \
+	src/ConfigTemplates.cxx src/ConfigTemplates.hxx \
+	src/ConfigOption.hxx \
 	src/CrossFade.cxx src/CrossFade.hxx \
 	src/cue/cue_parser.c src/cue/cue_parser.h \
 	src/decoder_error.h \
@@ -1052,7 +1053,8 @@ test_read_conf_LDADD = \
 	libfs.a \
 	$(GLIB_LIBS)
 test_read_conf_SOURCES = test/read_conf.cxx \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c
 
 test_run_resolver_LDADD = \
 	$(GLIB_LIBS)
@@ -1074,7 +1076,8 @@ test_DumpDatabase_SOURCES = test/DumpDatabase.cxx \
 	src/Tag.cxx src/TagNames.c src/TagPool.cxx src/TagSave.cxx \
 	src/SongFilter.cxx \
 	src/TextFile.cxx \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c
 
 test_run_input_LDADD = \
 	$(INPUT_LIBS) \
@@ -1085,7 +1088,8 @@ test_run_input_LDADD = \
 test_run_input_SOURCES = test/run_input.cxx \
 	test/stdbin.h \
 	src/IOThread.cxx \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c\
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c\
 	src/Tag.cxx src/TagNames.c src/TagPool.cxx src/TagSave.cxx \
 	src/uri.c \
 	src/fd_util.c
@@ -1101,7 +1105,8 @@ test_visit_archive_LDADD = \
 test_visit_archive_SOURCES = test/visit_archive.cxx \
 	src/IOThread.cxx \
 	src/InputStream.cxx \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c \
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c \
 	src/Tag.cxx src/TagNames.c src/TagPool.cxx \
 	src/uri.c \
 	src/fd_util.c
@@ -1122,7 +1127,8 @@ test_dump_text_file_LDADD = \
 test_dump_text_file_SOURCES = test/dump_text_file.cxx \
 	test/stdbin.h \
 	src/IOThread.cxx \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c\
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c\
 	src/Tag.cxx src/TagNames.c src/TagPool.cxx \
 	src/text_input_stream.c \
 	src/uri.c \
@@ -1142,7 +1148,8 @@ test_dump_playlist_LDADD = \
 test_dump_playlist_SOURCES = test/dump_playlist.cxx \
 	$(DECODER_SRC) \
 	src/IOThread.cxx \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c\
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c\
 	src/uri.c \
 	src/Song.cxx src/Tag.cxx src/TagNames.c src/TagPool.cxx src/TagSave.cxx \
 	src/tag_handler.c src/TagFile.cxx \
@@ -1170,7 +1177,8 @@ test_run_decoder_LDADD = \
 test_run_decoder_SOURCES = test/run_decoder.cxx \
 	test/stdbin.h \
 	src/IOThread.cxx \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c \
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c \
 	src/Tag.cxx src/TagNames.c src/TagPool.cxx src/tag_handler.c \
 	src/ReplayGainInfo.cxx \
 	src/uri.c \
@@ -1194,7 +1202,8 @@ test_read_tags_LDADD = \
 	$(GLIB_LIBS)
 test_read_tags_SOURCES = test/read_tags.cxx \
 	src/IOThread.cxx \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c \
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c \
 	src/Tag.cxx src/TagNames.c src/TagPool.cxx src/tag_handler.c \
 	src/ReplayGainInfo.cxx \
 	src/uri.c \
@@ -1222,7 +1231,8 @@ test_run_filter_SOURCES = test/run_filter.cxx \
 	test/stdbin.h \
 	src/filter_plugin.c \
 	src/filter_registry.c \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c \
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c \
 	src/audio_check.c \
 	src/audio_format.c \
 	src/audio_parser.c \
@@ -1241,8 +1251,8 @@ if ENABLE_ENCODER
 noinst_PROGRAMS += test/run_encoder
 test_run_encoder_SOURCES = test/run_encoder.c \
 	test/stdbin.h \
-	src/ConfigFile.cxx src/tokenizer.c \
-	src/utils.c src/string_util.c \
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c \
 	src/Tag.cxx src/TagNames.c src/TagPool.cxx \
 	src/audio_check.c \
 	src/audio_format.c \
@@ -1260,9 +1270,8 @@ if ENABLE_VORBIS_ENCODER
 noinst_PROGRAMS += test/test_vorbis_encoder
 test_test_vorbis_encoder_SOURCES = test/test_vorbis_encoder.c \
 	test/stdbin.h \
-	src/ConfigFile.cxx src/tokenizer.c \
-	src/utils.c \
-	src/string_util.c \
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c \
 	src/Tag.cxx src/TagNames.c src/TagPool.cxx \
 	src/audio_check.c \
 	src/audio_format.c \
@@ -1316,7 +1325,8 @@ test_run_output_LDADD = $(MPD_LIBS) \
 test_run_output_SOURCES = test/run_output.cxx \
 	test/FakeReplayGainConfig.cxx \
 	test/stdbin.h \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c \
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c \
 	src/IOThread.cxx \
 	src/audio_check.c \
 	src/audio_format.c \
@@ -1345,7 +1355,8 @@ test_read_mixer_LDADD = \
 	libfs.a \
 	$(GLIB_LIBS)
 test_read_mixer_SOURCES = test/read_mixer.cxx \
-	src/ConfigFile.cxx src/tokenizer.c src/utils.c src/string_util.c \
+	src/ConfigFile.cxx src/ConfigTemplates.cxx \
+	src/tokenizer.c src/utils.c src/string_util.c \
 	src/mixer_control.c src/mixer_api.c \
 	src/filter_plugin.c \
 	src/filter/volume_filter_plugin.c \
diff --git a/src/ConfigFile.cxx b/src/ConfigFile.cxx
index 2c8238c2f..ee2501ffc 100644
--- a/src/ConfigFile.cxx
+++ b/src/ConfigFile.cxx
@@ -46,7 +46,7 @@ extern "C" {
 
 #define CONF_COMMENT		'#'
 
-static GSList *config_params[G_N_ELEMENTS(config_templates)];
+static GSList *config_params[unsigned(CONF_MAX)];
 
 static bool
 get_bool(const char *value, bool *value_r)
@@ -309,8 +309,8 @@ ReadConfigFile(const Path &path, GError **error_r)
 		/* get the definition of that option, and check the
 		   "repeatable" flag */
 
-		int i = ConfigFindByName(name);
-		if (i < 0) {
+		const ConfigOption o = ParseConfigOptionName(name);
+		if (o == CONF_MAX) {
 			g_set_error(error_r, config_quark(), 0,
 				    "unrecognized parameter in config file at "
 				    "line %i: %s\n", count, name);
@@ -318,6 +318,7 @@ ReadConfigFile(const Path &path, GError **error_r)
 			return false;
 		}
 
+		const unsigned i = ParseConfigOptionName(name);
 		const ConfigTemplate &option = config_templates[i];
 		GSList *&params = config_params[i];
 
@@ -396,15 +397,9 @@ ReadConfigFile(const Path &path, GError **error_r)
 }
 
 const struct config_param *
-config_get_next_param(const char *name, const struct config_param * last)
+config_get_next_param(ConfigOption option, const struct config_param * last)
 {
-	struct config_param *param;
-
-	int i = ConfigFindByName(name);
-	if (i < 0)
-		return NULL;
-
-	GSList *node = config_params[i];
+	GSList *node = config_params[unsigned(option)];
 
 	if (last) {
 		node = g_slist_find(node, last);
@@ -417,15 +412,15 @@ config_get_next_param(const char *name, const struct config_param * last)
 	if (node == NULL)
 		return NULL;
 
-	param = (struct config_param *)node->data;
+	struct config_param *param = (struct config_param *)node->data;
 	param->used = true;
 	return param;
 }
 
 const char *
-config_get_string(const char *name, const char *default_value)
+config_get_string(ConfigOption option, const char *default_value)
 {
-	const struct config_param *param = config_get_param(name);
+	const struct config_param *param = config_get_param(option);
 
 	if (param == NULL)
 		return default_value;
@@ -434,28 +429,28 @@ config_get_string(const char *name, const char *default_value)
 }
 
 char *
-config_dup_path(const char *name, GError **error_r)
+config_dup_path(ConfigOption option, GError **error_r)
 {
 	assert(error_r != NULL);
 	assert(*error_r == NULL);
 
-	const struct config_param *param = config_get_param(name);
+	const struct config_param *param = config_get_param(option);
 	if (param == NULL)
 		return NULL;
 
 	char *path = parsePath(param->value, error_r);
 	if (G_UNLIKELY(path == NULL))
 		g_prefix_error(error_r,
-			       "Invalid path in \"%s\" at line %i: ",
-			       name, param->line);
+			       "Invalid path at line %i: ",
+			       param->line);
 
 	return path;
 }
 
 unsigned
-config_get_unsigned(const char *name, unsigned default_value)
+config_get_unsigned(ConfigOption option, unsigned default_value)
 {
-	const struct config_param *param = config_get_param(name);
+	const struct config_param *param = config_get_param(option);
 	long value;
 	char *endptr;
 
@@ -471,9 +466,9 @@ config_get_unsigned(const char *name, unsigned default_value)
 }
 
 unsigned
-config_get_positive(const char *name, unsigned default_value)
+config_get_positive(ConfigOption option, unsigned default_value)
 {
-	const struct config_param *param = config_get_param(name);
+	const struct config_param *param = config_get_param(option);
 	long value;
 	char *endptr;
 
@@ -507,9 +502,10 @@ config_get_block_param(const struct config_param * param, const char *name)
 	return NULL;
 }
 
-bool config_get_bool(const char *name, bool default_value)
+bool
+config_get_bool(ConfigOption option, bool default_value)
 {
-	const struct config_param *param = config_get_param(name);
+	const struct config_param *param = config_get_param(option);
 	bool success, value;
 
 	if (param == NULL)
@@ -517,9 +513,9 @@ bool config_get_bool(const char *name, bool default_value)
 
 	success = get_bool(param->value, &value);
 	if (!success)
-		MPD_ERROR("%s is not a boolean value (yes, true, 1) or "
+		MPD_ERROR("Expected boolean value (yes, true, 1) or "
 			  "(no, false, 0) on line %i\n",
-			  name, param->line);
+			  param->line);
 
 	return value;
 }
diff --git a/src/ConfigOption.hxx b/src/ConfigOption.hxx
new file mode 100644
index 000000000..21a3a02e4
--- /dev/null
+++ b/src/ConfigOption.hxx
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_OPTION_HXX
+#define MPD_CONFIG_OPTION_HXX
+
+#include "gcc.h"
+
+enum ConfigOption {
+	CONF_MUSIC_DIR,
+	CONF_PLAYLIST_DIR,
+	CONF_FOLLOW_INSIDE_SYMLINKS,
+	CONF_FOLLOW_OUTSIDE_SYMLINKS,
+	CONF_DB_FILE,
+	CONF_STICKER_FILE,
+	CONF_LOG_FILE,
+	CONF_PID_FILE,
+	CONF_STATE_FILE,
+	CONF_RESTORE_PAUSED,
+	CONF_USER,
+	CONF_GROUP,
+	CONF_BIND_TO_ADDRESS,
+	CONF_PORT,
+	CONF_LOG_LEVEL,
+	CONF_ZEROCONF_NAME,
+	CONF_ZEROCONF_ENABLED,
+	CONF_PASSWORD,
+	CONF_DEFAULT_PERMS,
+	CONF_AUDIO_OUTPUT,
+	CONF_AUDIO_OUTPUT_FORMAT,
+	CONF_MIXER_TYPE,
+	CONF_REPLAYGAIN,
+	CONF_REPLAYGAIN_PREAMP,
+	CONF_REPLAYGAIN_MISSING_PREAMP,
+	CONF_REPLAYGAIN_LIMIT,
+	CONF_VOLUME_NORMALIZATION,
+	CONF_SAMPLERATE_CONVERTER,
+	CONF_AUDIO_BUFFER_SIZE,
+	CONF_BUFFER_BEFORE_PLAY,
+	CONF_HTTP_PROXY_HOST,
+	CONF_HTTP_PROXY_PORT,
+	CONF_HTTP_PROXY_USER,
+	CONF_HTTP_PROXY_PASSWORD,
+	CONF_CONN_TIMEOUT,
+	CONF_MAX_CONN,
+	CONF_MAX_PLAYLIST_LENGTH,
+	CONF_MAX_COMMAND_LIST_SIZE,
+	CONF_MAX_OUTPUT_BUFFER_SIZE,
+	CONF_FS_CHARSET,
+	CONF_ID3V1_ENCODING,
+	CONF_METADATA_TO_USE,
+	CONF_SAVE_ABSOLUTE_PATHS,
+	CONF_DECODER,
+	CONF_INPUT,
+	CONF_GAPLESS_MP3_PLAYBACK,
+	CONF_PLAYLIST_PLUGIN,
+	CONF_AUTO_UPDATE,
+	CONF_AUTO_UPDATE_DEPTH,
+	CONF_DESPOTIFY_USER,
+	CONF_DESPOTIFY_PASSWORD,
+	CONF_DESPOTIFY_HIGH_BITRATE,
+	CONF_AUDIO_FILTER,
+	CONF_DATABASE,
+	CONF_MAX
+};
+
+/**
+ * @return #CONF_MAX if not found
+ */
+gcc_pure
+enum ConfigOption
+ParseConfigOptionName(const char *name);
+
+#endif
diff --git a/src/ConfigTemplates.cxx b/src/ConfigTemplates.cxx
new file mode 100644
index 000000000..27726ddb7
--- /dev/null
+++ b/src/ConfigTemplates.cxx
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ConfigTemplates.hxx"
+#include "ConfigOption.hxx"
+
+#include <string.h>
+
+const ConfigTemplate config_templates[] = {
+	{ "music_directory", false, false },
+	{ "playlist_directory", false, false },
+	{ "follow_inside_symlinks", false, false },
+	{ "follow_outside_symlinks", false, false },
+	{ "db_file", false, false },
+	{ "sticker_file", false, false },
+	{ "log_file", false, false },
+	{ "pid_file", false, false },
+	{ "state_file", false, false },
+	{ "restore_paused", false, false },
+	{ "user", false, false },
+	{ "group", false, false },
+	{ "bind_to_address", true, false },
+	{ "port", false, false },
+	{ "log_level", false, false },
+	{ "zeroconf_name", false, false },
+	{ "zeroconf_enabled", false, false },
+	{ "password", true, false },
+	{ "default_permissions", false, false },
+	{ "audio_output", true, true },
+	{ "audio_output_format", false, false },
+	{ "mixer_type", false, false },
+	{ "replaygain", false, false },
+	{ "replaygain_preamp", false, false },
+	{ "replaygain_missing_preamp", false, false },
+	{ "replaygain_limit", false, false },
+	{ "volume_normalization", false, false },
+	{ "samplerate_converter", false, false },
+	{ "audio_buffer_size", false, false },
+	{ "buffer_before_play", false, false },
+	{ "http_proxy_host", false, false },
+	{ "http_proxy_port", false, false },
+	{ "http_proxy_user", false, false },
+	{ "http_proxy_password", false, false },
+	{ "connection_timeout", false, false },
+	{ "max_connections", false, false },
+	{ "max_playlist_length", false, false },
+	{ "max_command_list_size", false, false },
+	{ "max_output_buffer_size", false, false },
+	{ "filesystem_charset", false, false },
+	{ "id3vtrue_encoding", false, false },
+	{ "metadata_to_use", false, false },
+	{ "save_absolute_paths_in_playlists", false, false },
+	{ "decoder", true, true },
+	{ "input", true, true },
+	{ "gapless_mp3_playback", false, false },
+	{ "playlist_plugin", true, true },
+	{ "auto_update", false, false },
+	{ "auto_update_depth", false, false },
+	{ "despotify_user", false, false },
+	{ "despotify_password", false, false},
+	{ "despotify_high_bitrate", false, false },
+	{ "filter", true, true },
+	{ "database", false, true },
+};
+
+static constexpr unsigned n_config_templates =
+	sizeof(config_templates) / sizeof(config_templates[0]);
+
+static_assert(n_config_templates == unsigned(CONF_MAX),
+	      "Wrong number of config_templates");
+
+ConfigOption
+ParseConfigOptionName(const char *name)
+{
+	for (unsigned i = 0; i < n_config_templates; ++i)
+		if (strcmp(config_templates[i].name, name) == 0)
+			return ConfigOption(i);
+
+	return CONF_MAX;
+}
diff --git a/src/ConfigTemplates.hxx b/src/ConfigTemplates.hxx
index 8592070cd..4f5460460 100644
--- a/src/ConfigTemplates.hxx
+++ b/src/ConfigTemplates.hxx
@@ -17,11 +17,10 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#include "gcc.h"
+#ifndef MPD_CONFIG_TEMPLATES_HXX
+#define MPD_CONFIG_TEMPLATES_HXX
 
-#include <glib.h>
-
-#include <string.h>
+#include "ConfigOption.hxx"
 
 struct ConfigTemplate {
 	const char *const name;
@@ -29,70 +28,6 @@ struct ConfigTemplate {
 	const bool block;
 };
 
-static constexpr struct ConfigTemplate config_templates[] = {
-	{ CONF_MUSIC_DIR, false, false },
-	{ CONF_PLAYLIST_DIR, false, false },
-	{ CONF_FOLLOW_INSIDE_SYMLINKS, false, false },
-	{ CONF_FOLLOW_OUTSIDE_SYMLINKS, false, false },
-	{ CONF_DB_FILE, false, false },
-	{ CONF_STICKER_FILE, false, false },
-	{ CONF_LOG_FILE, false, false },
-	{ CONF_PID_FILE, false, false },
-	{ CONF_STATE_FILE, false, false },
-	{ "restore_paused", false, false },
-	{ CONF_USER, false, false },
-	{ CONF_GROUP, false, false },
-	{ CONF_BIND_TO_ADDRESS, true, false },
-	{ CONF_PORT, false, false },
-	{ CONF_LOG_LEVEL, false, false },
-	{ CONF_ZEROCONF_NAME, false, false },
-	{ CONF_ZEROCONF_ENABLED, false, false },
-	{ CONF_PASSWORD, true, false },
-	{ CONF_DEFAULT_PERMS, false, false },
-	{ CONF_AUDIO_OUTPUT, true, true },
-	{ CONF_AUDIO_OUTPUT_FORMAT, false, false },
-	{ CONF_MIXER_TYPE, false, false },
-	{ CONF_REPLAYGAIN, false, false },
-	{ CONF_REPLAYGAIN_PREAMP, false, false },
-	{ CONF_REPLAYGAIN_MISSING_PREAMP, false, false },
-	{ CONF_REPLAYGAIN_LIMIT, false, false },
-	{ CONF_VOLUME_NORMALIZATION, false, false },
-	{ CONF_SAMPLERATE_CONVERTER, false, false },
-	{ CONF_AUDIO_BUFFER_SIZE, false, false },
-	{ CONF_BUFFER_BEFORE_PLAY, false, false },
-	{ CONF_HTTP_PROXY_HOST, false, false },
-	{ CONF_HTTP_PROXY_PORT, false, false },
-	{ CONF_HTTP_PROXY_USER, false, false },
-	{ CONF_HTTP_PROXY_PASSWORD, false, false },
-	{ CONF_CONN_TIMEOUT, false, false },
-	{ CONF_MAX_CONN, false, false },
-	{ CONF_MAX_PLAYLIST_LENGTH, false, false },
-	{ CONF_MAX_COMMAND_LIST_SIZE, false, false },
-	{ CONF_MAX_OUTPUT_BUFFER_SIZE, false, false },
-	{ CONF_FS_CHARSET, false, false },
-	{ CONF_ID3V1_ENCODING, false, false },
-	{ CONF_METADATA_TO_USE, false, false },
-	{ CONF_SAVE_ABSOLUTE_PATHS, false, false },
-	{ CONF_DECODER, true, true },
-	{ CONF_INPUT, true, true },
-	{ CONF_GAPLESS_MP3_PLAYBACK, false, false },
-	{ CONF_PLAYLIST_PLUGIN, true, true },
-	{ CONF_AUTO_UPDATE, false, false },
-	{ CONF_AUTO_UPDATE_DEPTH, false, false },
-	{ CONF_DESPOTIFY_USER, false, false },
-	{ CONF_DESPOTIFY_PASSWORD, false, false},
-	{ CONF_DESPOTIFY_HIGH_BITRATE, false, false },
-	{ "filter", true, true },
-	{ "database", false, true },
-};
+extern const ConfigTemplate config_templates[];
 
-gcc_pure
-static int
-ConfigFindByName(const char *name)
-{
-	for (unsigned i = 0; i < G_N_ELEMENTS(config_templates); ++i)
-		if (strcmp(config_templates[i].name, name) == 0)
-			return i;
-
-	return -1;
-}
+#endif
diff --git a/src/Log.cxx b/src/Log.cxx
index 76b365778..8a50e1efb 100644
--- a/src/Log.cxx
+++ b/src/Log.cxx
@@ -261,8 +261,7 @@ log_init(bool verbose, bool use_stdout, GError **error_r)
 			return true;
 #else
 			g_set_error(error_r, log_quark(), 0,
-				    "config parameter \"%s\" not found",
-				    CONF_LOG_FILE);
+				    "config parameter 'log_file' not found");
 			return false;
 #endif
 #ifdef HAVE_SYSLOG
diff --git a/src/Main.cxx b/src/Main.cxx
index ecea59843..a22ee6313 100644
--- a/src/Main.cxx
+++ b/src/Main.cxx
@@ -167,12 +167,11 @@ glue_mapper_init(GError **error_r)
 static bool
 glue_db_init_and_load(void)
 {
-	const struct config_param *param = config_get_param("database");
+	const struct config_param *param = config_get_param(CONF_DATABASE);
 	const struct config_param *path = config_get_param(CONF_DB_FILE);
 
 	if (param != NULL && path != NULL)
-		g_message("Found both 'database' and '" CONF_DB_FILE
-			  "' setting - ignoring the latter");
+		g_message("Found both 'database' and 'db_file' setting - ignoring the latter");
 
 	GError *error = NULL;
 	bool ret;
@@ -180,10 +179,10 @@ glue_db_init_and_load(void)
 	if (!mapper_has_music_directory()) {
 		if (param != NULL)
 			g_message("Found database setting without "
-				  CONF_MUSIC_DIR " - disabling database");
+				  "music_directory - disabling database");
 		if (path != NULL)
-			g_message("Found " CONF_DB_FILE " setting without "
-				  CONF_MUSIC_DIR " - disabling database");
+			g_message("Found db_file setting without "
+				  "music_directory - disabling database");
 		return true;
 	}
 
diff --git a/src/OutputInit.cxx b/src/OutputInit.cxx
index 2890b3f29..4e352f287 100644
--- a/src/OutputInit.cxx
+++ b/src/OutputInit.cxx
@@ -91,7 +91,8 @@ audio_output_mixer_type(const struct config_param *param)
 
 	/* fall back to the global "mixer_type" setting (also
 	   deprecated) */
-	return mixer_type_parse(config_get_string("mixer_type", "hardware"));
+	return mixer_type_parse(config_get_string(CONF_MIXER_TYPE,
+						  "hardware"));
 }
 
 static struct mixer *
@@ -312,8 +313,7 @@ audio_output_new(const struct config_param *param,
 			return nullptr;
 		}
 	} else {
-		g_warning("No \"%s\" defined in config file\n",
-			  CONF_AUDIO_OUTPUT);
+		g_warning("No 'audio_output' defined in config file\n");
 
 		plugin = audio_output_detect(error_r);
 		if (plugin == NULL)
diff --git a/src/PlaylistState.cxx b/src/PlaylistState.cxx
index 212b52868..d03de0a16 100644
--- a/src/PlaylistState.cxx
+++ b/src/PlaylistState.cxx
@@ -180,7 +180,7 @@ playlist_state_restore(const char *line, TextFile &file,
 			current = 0;
 
 		if (state == PLAYER_STATE_PLAY &&
-		    config_get_bool("restore_paused", false))
+		    config_get_bool(CONF_RESTORE_PAUSED, false))
 			/* the user doesn't want MPD to auto-start
 			   playback after startup; fall back to
 			   "pause" */
diff --git a/src/audio_config.c b/src/audio_config.c
index fe7484fb4..bae3c184b 100644
--- a/src/audio_config.c
+++ b/src/audio_config.c
@@ -47,7 +47,6 @@ void initAudioConfig(void)
 	ret = audio_format_parse(&configured_audio_format, param->value,
 				 true, &error);
 	if (!ret)
-		MPD_ERROR("error parsing \"%s\" at line %i: %s",
-			  CONF_AUDIO_OUTPUT_FORMAT, param->line,
-			  error->message);
+		MPD_ERROR("error parsing line %i: %s",
+			  param->line, error->message);
 }
diff --git a/src/conf.h b/src/conf.h
index ca60591d6..61c6cd9f7 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -20,62 +20,12 @@
 #ifndef MPD_CONF_H
 #define MPD_CONF_H
 
+#include "ConfigOption.hxx"
+#include "gcc.h"
+
 #include <stdbool.h>
 #include <glib.h>
 
-#define CONF_MUSIC_DIR                  "music_directory"
-#define CONF_PLAYLIST_DIR               "playlist_directory"
-#define CONF_FOLLOW_INSIDE_SYMLINKS     "follow_inside_symlinks"
-#define CONF_FOLLOW_OUTSIDE_SYMLINKS    "follow_outside_symlinks"
-#define CONF_DB_FILE                    "db_file"
-#define CONF_STICKER_FILE               "sticker_file"
-#define CONF_LOG_FILE                   "log_file"
-#define CONF_PID_FILE                   "pid_file"
-#define CONF_STATE_FILE                 "state_file"
-#define CONF_USER                       "user"
-#define CONF_GROUP                      "group"
-#define CONF_BIND_TO_ADDRESS            "bind_to_address"
-#define CONF_PORT                       "port"
-#define CONF_LOG_LEVEL                  "log_level"
-#define CONF_ZEROCONF_NAME              "zeroconf_name"
-#define CONF_ZEROCONF_ENABLED           "zeroconf_enabled"
-#define CONF_PASSWORD                   "password"
-#define CONF_DEFAULT_PERMS              "default_permissions"
-#define CONF_AUDIO_OUTPUT               "audio_output"
-#define CONF_AUDIO_FILTER               "filter"
-#define CONF_AUDIO_OUTPUT_FORMAT        "audio_output_format"
-#define CONF_MIXER_TYPE                 "mixer_type"
-#define CONF_REPLAYGAIN                 "replaygain"
-#define CONF_REPLAYGAIN_PREAMP          "replaygain_preamp"
-#define CONF_REPLAYGAIN_MISSING_PREAMP  "replaygain_missing_preamp"
-#define CONF_REPLAYGAIN_LIMIT           "replaygain_limit"
-#define CONF_VOLUME_NORMALIZATION       "volume_normalization"
-#define CONF_SAMPLERATE_CONVERTER       "samplerate_converter"
-#define CONF_AUDIO_BUFFER_SIZE          "audio_buffer_size"
-#define CONF_BUFFER_BEFORE_PLAY         "buffer_before_play"
-#define CONF_HTTP_PROXY_HOST            "http_proxy_host"
-#define CONF_HTTP_PROXY_PORT            "http_proxy_port"
-#define CONF_HTTP_PROXY_USER            "http_proxy_user"
-#define CONF_HTTP_PROXY_PASSWORD        "http_proxy_password"
-#define CONF_CONN_TIMEOUT               "connection_timeout"
-#define CONF_MAX_CONN                   "max_connections"
-#define CONF_MAX_PLAYLIST_LENGTH        "max_playlist_length"
-#define CONF_MAX_COMMAND_LIST_SIZE      "max_command_list_size"
-#define CONF_MAX_OUTPUT_BUFFER_SIZE     "max_output_buffer_size"
-#define CONF_FS_CHARSET                 "filesystem_charset"
-#define CONF_ID3V1_ENCODING             "id3v1_encoding"
-#define CONF_METADATA_TO_USE            "metadata_to_use"
-#define CONF_SAVE_ABSOLUTE_PATHS        "save_absolute_paths_in_playlists"
-#define CONF_DECODER                    "decoder"
-#define CONF_INPUT                      "input"
-#define CONF_GAPLESS_MP3_PLAYBACK       "gapless_mp3_playback"
-#define CONF_PLAYLIST_PLUGIN            "playlist_plugin"
-#define CONF_AUTO_UPDATE                "auto_update"
-#define CONF_AUTO_UPDATE_DEPTH          "auto_update_depth"
-#define CONF_DESPOTIFY_USER             "despotify_user"
-#define CONF_DESPOTIFY_PASSWORD         "despotify_password"
-#define CONF_DESPOTIFY_HIGH_BITRATE     "despotify_high_bitrate"
-
 #define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
 #define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS false
 
@@ -144,13 +94,14 @@ G_BEGIN_DECLS
    set _last_ to NULL to get first entry */
 G_GNUC_PURE
 const struct config_param *
-config_get_next_param(const char *name, const struct config_param *last);
+config_get_next_param(enum ConfigOption option,
+		      const struct config_param *last);
 
 G_GNUC_PURE
 static inline const struct config_param *
-config_get_param(const char *name)
+config_get_param(enum ConfigOption option)
 {
-	return config_get_next_param(name, NULL);
+	return config_get_next_param(option, NULL);
 }
 
 /* Note on G_GNUC_PURE: Some of the functions declared pure are not
@@ -162,7 +113,7 @@ config_get_param(const char *name)
 
 G_GNUC_PURE
 const char *
-config_get_string(const char *name, const char *default_value);
+config_get_string(enum ConfigOption option, const char *default_value);
 
 /**
  * Returns an optional configuration variable which contains an
@@ -174,22 +125,22 @@ config_get_string(const char *name, const char *default_value);
  */
 G_GNUC_MALLOC
 char *
-config_dup_path(const char *name, GError **error_r);
+config_dup_path(enum ConfigOption option, GError **error_r);
 
 G_GNUC_PURE
 unsigned
-config_get_unsigned(const char *name, unsigned default_value);
+config_get_unsigned(enum ConfigOption option, unsigned default_value);
 
 G_GNUC_PURE
 unsigned
-config_get_positive(const char *name, unsigned default_value);
+config_get_positive(enum ConfigOption option, unsigned default_value);
 
 G_GNUC_PURE
 const struct block_param *
 config_get_block_param(const struct config_param *param, const char *name);
 
 G_GNUC_PURE
-bool config_get_bool(const char *name, bool default_value);
+bool config_get_bool(enum ConfigOption option, bool default_value);
 
 G_GNUC_PURE
 const char *
diff --git a/test/dump_rva2.c b/test/dump_rva2.c
index 4a726518a..bb47fc137 100644
--- a/test/dump_rva2.c
+++ b/test/dump_rva2.c
@@ -33,7 +33,8 @@
 #include <stdlib.h>
 
 const char *
-config_get_string(G_GNUC_UNUSED const char *name, const char *default_value)
+config_get_string(gcc_unused enum ConfigOption option,
+		  const char *default_value)
 {
 	return default_value;
 }
diff --git a/test/read_conf.cxx b/test/read_conf.cxx
index a9135d892..8759398da 100644
--- a/test/read_conf.cxx
+++ b/test/read_conf.cxx
@@ -57,7 +57,10 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
-	const char *value = config_get_string(name, NULL);
+	ConfigOption option = ParseConfigOptionName(name);
+	const char *value = option != CONF_MAX
+		? config_get_string(option, nullptr)
+		: nullptr;
 	int ret;
 	if (value != NULL) {
 		g_print("%s\n", value);
diff --git a/test/run_convert.c b/test/run_convert.c
index 21be83f73..bdb3d2cf0 100644
--- a/test/run_convert.c
+++ b/test/run_convert.c
@@ -48,7 +48,8 @@ my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
 }
 
 const char *
-config_get_string(G_GNUC_UNUSED const char *name, const char *default_value)
+config_get_string(gcc_unused enum ConfigOption option,
+		  const char *default_value)
 {
 	return default_value;
 }
diff --git a/test/run_filter.cxx b/test/run_filter.cxx
index b05e458aa..dbf6caa97 100644
--- a/test/run_filter.cxx
+++ b/test/run_filter.cxx
@@ -56,11 +56,11 @@ my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
 }
 
 static const struct config_param *
-find_named_config_block(const char *block, const char *name)
+find_named_config_block(ConfigOption option, const char *name)
 {
 	const struct config_param *param = NULL;
 
-	while ((param = config_get_next_param(block, param)) != NULL) {
+	while ((param = config_get_next_param(option, param)) != NULL) {
 		const char *current_name =
 			config_get_block_string(param, "name", NULL);
 		if (current_name != NULL && strcmp(current_name, name) == 0)
@@ -77,7 +77,7 @@ load_filter(const char *name)
 	struct filter *filter;
 	GError *error = NULL;
 
-	param = find_named_config_block("filter", name);
+	param = find_named_config_block(CONF_AUDIO_FILTER, name);
 	if (param == NULL) {
 		g_printerr("No such configured filter: %s\n", name);
 		return nullptr;
diff --git a/test/run_output.cxx b/test/run_output.cxx
index 38f24fe5f..ec94beaad 100644
--- a/test/run_output.cxx
+++ b/test/run_output.cxx
@@ -81,11 +81,11 @@ filter_plugin_by_name(G_GNUC_UNUSED const char *name)
 }
 
 static const struct config_param *
-find_named_config_block(const char *block, const char *name)
+find_named_config_block(ConfigOption option, const char *name)
 {
 	const struct config_param *param = NULL;
 
-	while ((param = config_get_next_param(block, param)) != NULL) {
+	while ((param = config_get_next_param(option, param)) != NULL) {
 		const char *current_name =
 			config_get_block_string(param, "name", NULL);
 		if (current_name != NULL && strcmp(current_name, name) == 0)