diff --git a/README-extensions.rst b/README-extensions.rst
index da9ce85b..eb40f054 100644
--- a/README-extensions.rst
+++ b/README-extensions.rst
@@ -203,6 +203,46 @@ The ``${beta:${alpha:two}}`` construct first resolves the ``${alpha:two}`` refer
 the reference ``${beta:a}`` to the value 99.
 
 
+Default values
+-----------------
+
+References can now have a default value, if the reference couldn't get resolved, for example:
+
+.. code-block:: yaml
+
+  # nodes/node1.yml
+  parameters:
+    myvalue: ${not:existing:ref||default}
+
+``reclass.py --nodeinfo node1`` then gives:
+
+.. code-block:: yaml
+
+  parameters:
+    myvalue: default
+
+The ``${not:existing:ref||default}`` construct searches for a value at ``not:existing:ref``, and then because it can't find any, it will take the default value.
+
+This works for nested references as well:
+
+.. code-block:: yaml
+
+  # nodes/node1.yml
+  parameters:
+    default: 123
+    a: ${not:existing:ref||${default}}
+    b: ${not:existing:ref||${not:existing:default||fallback}}
+
+``reclass.py --nodeinfo node1`` then gives:
+
+.. code-block:: yaml
+
+  parameters:
+    default: 123
+    a: 123
+    b: fallback
+
+
 Ignore overwritten missing references
 -------------------------------------
 
diff --git a/reclass/datatypes/tests/test_parameters.py b/reclass/datatypes/tests/test_parameters.py
index 80fd8de1..d32dba3b 100644
--- a/reclass/datatypes/tests/test_parameters.py
+++ b/reclass/datatypes/tests/test_parameters.py
@@ -414,6 +414,20 @@ def test_nested_deep_references(self):
         p.interpolate()
         self.assertEqual(p.as_dict(), r)
 
+    def test_nested_references_default_exists(self):
+        values = {"a": "value", "b": "${not:existing||${a}}"}
+        reference = {"a": "value", "b": "value"}
+        parameters = Parameters(values, SETTINGS, '')
+        parameters.interpolate()
+        self.assertEqual(parameters.as_dict(), reference)
+
+    def test_nested_references_default_exists(self):
+        values = {"a": "value", "b": "${not:existing||${again||fallback}}"}
+        reference = {"a": "value", "b": "fallback"}
+        parameters = Parameters(values, SETTINGS, '')
+        parameters.interpolate()
+        self.assertEqual(parameters.as_dict(), reference)
+
     def test_stray_occurrence_overwrites_during_interpolation(self):
         p1 = Parameters({'r' : 1, 'b': '${r}'}, SETTINGS, '')
         p2 = Parameters({'b' : 2}, SETTINGS, '')
diff --git a/reclass/utils/dictpath.py b/reclass/utils/dictpath.py
index 70c7bb51..2afd60fb 100644
--- a/reclass/utils/dictpath.py
+++ b/reclass/utils/dictpath.py
@@ -67,6 +67,8 @@ def __init__(self, delim, contents=None):
         elif isinstance(contents, list):
             self._parts = contents
         elif isinstance(contents, six.string_types):
+            # parse the default value from contents, strip default section (||...) from parts            
+            contents, self.default = self._split_default(contents)
             self._parts = self._split_string(contents)
         elif isinstance(contents, tuple):
             self._parts = list(contents)
@@ -115,6 +117,18 @@ def _get_innermost_container(self, base):
     def _split_string(self, string):
         return re.split(r'(?<!\\)' + re.escape(self._delim), string)
 
+    def _split_default(self, string):
+        """
+        splits reference tag 'my:tag||default-value' into two parts
+        """
+        parts = string.split("||")
+        if not parts: 
+            return None # no contents at all
+        elif len(parts) == 1:
+            return parts[0], None # no default
+        else:
+            return parts[:-1][0], parts[-1] # contain '||' in contents and use just the last '||'
+
     def key_parts(self):
         return self._parts[:-1]
 
diff --git a/reclass/values/refitem.py b/reclass/values/refitem.py
index 64bf4503..73880815 100644
--- a/reclass/values/refitem.py
+++ b/reclass/values/refitem.py
@@ -29,6 +29,10 @@ def _resolve(self, ref, context):
         try:
             return path.get_value(context)
         except (KeyError, TypeError) as e:
+            if refpath.default is not None:
+                if refpath.default == '':
+                    refpath.default = None
+                return refpath.default
             raise ResolveError(ref)
 
     def render(self, context, inventory):
diff --git a/reclass/values/tests/test_refitem.py b/reclass/values/tests/test_refitem.py
index 65814782..692ec3d3 100644
--- a/reclass/values/tests/test_refitem.py
+++ b/reclass/values/tests/test_refitem.py
@@ -44,6 +44,16 @@ def test__resolve_ok(self):
 
         self.assertEquals(result, 1)
 
+    def test_default_resolve_ok(self):
+        reference = RefItem('', Settings({'delimiter': ':'}))
+        result = reference._resolve('non:existing||default', {'foo':'bar'})
+        self.assertEquals(result, 'default')
+
+    def test_default_exists_resolve_ok(self):
+        reference = RefItem('', Settings({'delimiter': ':'}))
+        result = reference._resolve('foo||default', {'foo':'bar'})
+        self.assertEquals(result, 'bar')
+
     def test__resolve_fails(self):
         refitem = RefItem('', Settings({'delimiter': ':'}))
         context = {'foo':{'bar': 1}}