diff --git a/compose/config/config.py b/compose/config/config.py
index b5646a479c0..239bbf5e000 100644
--- a/compose/config/config.py
+++ b/compose/config/config.py
@@ -13,7 +13,11 @@
     CircularReference,
     ComposeFileNotFound,
 )
-from .validation import validate_against_schema
+from .validation import (
+    validate_against_schema,
+    validate_service_names,
+    validate_top_level_object
+)
 
 
 DOCKER_CONFIG_KEYS = [
@@ -122,19 +126,26 @@ def get_config_path(base_dir):
     return os.path.join(path, winner)
 
 
+@validate_top_level_object
+@validate_service_names
+def pre_process_config(config):
+    """
+    Pre validation checks and processing of the config file to interpolate env
+    vars returning a config dict ready to be tested against the schema.
+    """
+    config = interpolate_environment_variables(config)
+    return config
+
+
 def load(config_details):
     config, working_dir, filename = config_details
-    if not isinstance(config, dict):
-        raise ConfigurationError(
-            "Top level object needs to be a dictionary. Check your .yml file that you have defined a service at the top level."
-        )
 
-    config = interpolate_environment_variables(config)
-    validate_against_schema(config)
+    processed_config = pre_process_config(config)
+    validate_against_schema(processed_config)
 
     service_dicts = []
 
-    for service_name, service_dict in list(config.items()):
+    for service_name, service_dict in list(processed_config.items()):
         loader = ServiceLoader(working_dir=working_dir, filename=filename)
         service_dict = loader.make_service_dict(service_name, service_dict)
         validate_paths(service_dict)
diff --git a/compose/config/validation.py b/compose/config/validation.py
index 36fd03b5f20..26f3ca8ec7a 100644
--- a/compose/config/validation.py
+++ b/compose/config/validation.py
@@ -1,3 +1,4 @@
+from functools import wraps
 import os
 
 from docker.utils.ports import split_port
@@ -36,6 +37,29 @@ def format_ports(instance):
     return True
 
 
+def validate_service_names(func):
+    @wraps(func)
+    def func_wrapper(config):
+        for service_name in config.keys():
+            if type(service_name) is int:
+                raise ConfigurationError(
+                    "Service name: {} needs to be a string, eg '{}'".format(service_name, service_name)
+                )
+        return func(config)
+    return func_wrapper
+
+
+def validate_top_level_object(func):
+    @wraps(func)
+    def func_wrapper(config):
+        if not isinstance(config, dict):
+            raise ConfigurationError(
+                "Top level object needs to be a dictionary. Check your .yml file that you have defined a service at the top level."
+            )
+        return func(config)
+    return func_wrapper
+
+
 def get_unsupported_config_msg(service_name, error_key):
     msg = "Unsupported config option for '{}' service: '{}'".format(service_name, error_key)
     if error_key in DOCKER_CONFIG_HINTS:
diff --git a/tests/unit/config_test.py b/tests/unit/config_test.py
index c8a2d0db14b..553e85beb8a 100644
--- a/tests/unit/config_test.py
+++ b/tests/unit/config_test.py
@@ -64,6 +64,17 @@ def test_config_invalid_service_names(self):
                     )
                 )
 
+    def test_config_integer_service_name_raise_validation_error(self):
+        expected_error_msg = "Service name: 1 needs to be a string, eg '1'"
+        with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
+            config.load(
+                config.ConfigDetails(
+                    {1: {'image': 'busybox'}},
+                    'working_dir',
+                    'filename.yml'
+                )
+            )
+
     def test_config_valid_service_names(self):
         for valid_name in ['_', '-', '.__.', '_what-up.', 'what_.up----', 'whatup']:
             config.load(