diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb
index ae82c0db3f1ce40f0bc4de001d4ce0e42e5e8d5c..20f5f8e2ff8c181e571f7fcaf3dbe8fb4c53fd86 100644
--- a/lib/gitlab/ci/config.rb
+++ b/lib/gitlab/ci/config.rb
@@ -15,6 +15,7 @@ module Gitlab
 
         @global = Node::Global.new(@config)
         @global.process!
+        @global.validate!
       end
 
       def valid?
diff --git a/lib/gitlab/ci/config/node/configurable.rb b/lib/gitlab/ci/config/node/configurable.rb
index 88403a9de1e81167db63dff1acf5b3621b0c114c..e5780c60e70a9961412430b3979e29aa8cc7e0f4 100644
--- a/lib/gitlab/ci/config/node/configurable.rb
+++ b/lib/gitlab/ci/config/node/configurable.rb
@@ -25,7 +25,7 @@ module Gitlab
 
           private
 
-          def create_node(key, factory)
+          def create(key, factory)
             factory
               .value(config[key])
               .with(key: key, parent: self, global: global)
@@ -50,12 +50,12 @@ module Gitlab
             def helpers(*nodes)
               nodes.each do |symbol|
                 define_method("#{symbol}_defined?") do
-                  @nodes[symbol].try(:defined?)
+                  @entries[symbol].try(:defined?)
                 end
 
                 define_method("#{symbol}_value") do
                   raise Entry::InvalidError unless valid?
-                  @nodes[symbol].try(:value)
+                  @entries[symbol].try(:value)
                 end
 
                 alias_method symbol.to_sym, "#{symbol}_value".to_sym
diff --git a/lib/gitlab/ci/config/node/entry.rb b/lib/gitlab/ci/config/node/entry.rb
index e8b0160edc13a8766790856ba50001fa2be60aa3..67e59ffb86ef4daf2ce470983a7a48b542fa14dc 100644
--- a/lib/gitlab/ci/config/node/entry.rb
+++ b/lib/gitlab/ci/config/node/entry.rb
@@ -13,7 +13,7 @@ module Gitlab
 
           def initialize(config, **attributes)
             @config = config
-            @nodes = {}
+            @entries = {}
 
             (@attributes = attributes).each do |attribute, value|
               public_send("#{attribute}=", value)
@@ -24,8 +24,18 @@ module Gitlab
           end
 
           def process!
-            compose! unless leaf?
-            @validator.validate(:processed) if valid?
+            return unless valid?
+
+            nodes.each do |key, essence|
+              @entries[key] = create(key, essence)
+            end
+
+            @entries.each_value(&:process!)
+          end
+
+          def validate!
+            @validator.validate(:after)
+            @entries.each_value(&:validate!)
           end
 
           def leaf?
@@ -37,7 +47,7 @@ module Gitlab
           end
 
           def descendants
-            @nodes.values
+            @entries.values
           end
 
           def ancestors
@@ -49,18 +59,18 @@ module Gitlab
           end
 
           def errors
-            @validator.messages + @nodes.values.flat_map(&:errors)
+            @validator.messages + @entries.values.flat_map(&:errors)
           end
 
           def value
             if leaf?
               @config
             else
-              meaningful = @nodes.select do |_key, value|
+              meaningful = @entries.select do |_key, value|
                 value.defined? && value.relevant?
               end
 
-              Hash[meaningful.map { |key, node| [key, node.value] }]
+              Hash[meaningful.map { |key, entry| [key, entry.value] }]
             end
           end
 
@@ -85,17 +95,7 @@ module Gitlab
 
           private
 
-          def compose!
-            return unless valid?
-
-            nodes.each do |key, essence|
-              @nodes[key] = create_node(key, essence)
-            end
-
-            @nodes.each_value(&:process!)
-          end
-
-          def create_node(key, essence)
+          def create(entry, essence)
             raise NotImplementedError
           end
         end
diff --git a/lib/gitlab/ci/config/node/jobs.rb b/lib/gitlab/ci/config/node/jobs.rb
index cba1fce4a4c904fe0665267b725a5b33b2852d8b..6199749a5084fc2a2695d98f762961ca6213ca37 100644
--- a/lib/gitlab/ci/config/node/jobs.rb
+++ b/lib/gitlab/ci/config/node/jobs.rb
@@ -10,7 +10,7 @@ module Gitlab
 
           validations do
             validates :config, type: Hash
-            validate :jobs_presence, on: :processed
+            validate :jobs_presence, on: :after
 
             def jobs_presence
               unless relevant?
@@ -24,12 +24,12 @@ module Gitlab
           end
 
           def relevant?
-            @nodes.values.any?(&:relevant?)
+            @entries.values.any?(&:relevant?)
           end
 
           private
 
-          def create_node(name, config)
+          def create(name, config)
             job_node(name).new(config, job_attributes(name))
           end
 
diff --git a/spec/lib/gitlab/ci/config/node/jobs_spec.rb b/spec/lib/gitlab/ci/config/node/jobs_spec.rb
index 7ec28b642b47869f54f67cf3aa864921c641cc6d..1ecc3e18d4ed931114f0a013ad86895116b1c7e7 100644
--- a/spec/lib/gitlab/ci/config/node/jobs_spec.rb
+++ b/spec/lib/gitlab/ci/config/node/jobs_spec.rb
@@ -35,7 +35,10 @@ describe Gitlab::Ci::Config::Node::Jobs do
           end
 
           context 'when processed' do
-            before { entry.process! }
+            before do
+              entry.process!
+              entry.validate!
+            end
 
             it 'returns error about no visible jobs defined' do
               expect(entry.errors)