diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2d33bad58868531c42a3d2db2110c6165a81d01e..b0010572833fbdeb72adbcbc8cbab8e0d51c875e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -61,6 +61,15 @@ update-knapsack:
   only:
     - master
 
+update-coverage:
+  <<: *knapsack-state
+  stage: post-test
+  script:
+    - bundle exec rake ci:simplecov:merge
+  artifacts:
+    paths:
+    - coverage/
+
 # Execute all testing suites
 
 .use-db: &use-db
@@ -83,6 +92,7 @@ update-knapsack:
   artifacts:
     paths:
     - knapsack/
+    - coverage/
 
 .spinach-knapsack: &spinach-knapsack
   stage: test
@@ -99,6 +109,7 @@ update-knapsack:
   artifacts:
     paths:
     - knapsack/
+    - coverage/
 
 rspec 0 20: *rspec-knapsack
 rspec 1 20: *rspec-knapsack
diff --git a/.simplecov b/.simplecov
deleted file mode 100644
index d979288df44bfa11d3bbc74b52bae975631a4dbf..0000000000000000000000000000000000000000
--- a/.simplecov
+++ /dev/null
@@ -1,4 +0,0 @@
-# .simplecov
-SimpleCov.start 'rails' do
-  merge_timeout 3600
-end
diff --git a/lib/tasks/ci/simplecov.rake b/lib/tasks/ci/simplecov.rake
new file mode 100644
index 0000000000000000000000000000000000000000..0c8322940ec952bfba167ea714008fb4bfe528ea
--- /dev/null
+++ b/lib/tasks/ci/simplecov.rake
@@ -0,0 +1,63 @@
+require 'simplecov'
+
+namespace :ci do
+  namespace :simplecov do
+    desc 'GitLab CI | Merge all coverage results and generate report'
+    task merge: :environment do
+      merged_result.format!
+    end
+
+    private
+
+    def read(file)
+      return unless File.exist?(file)
+      data = File.read(file)
+      return if data.nil? || data.length < 2
+      data
+    end
+
+    def load(file)
+      begin
+        JSON.parse(read(file))
+      rescue
+        {}
+      end
+    end
+
+    def files
+      Dir.glob(File.join(SimpleCov.coverage_path, '*/.resultset.json'))
+    end
+
+    def resultsfiles
+      files.map { |file| load(file) }
+    end
+
+    def resultsets
+      resultsfiles.reduce({}, :merge)
+    end
+
+    def all_results
+      results = []
+      resultsets.each do |command_name, data|
+        result = SimpleCov::Result.from_hash(command_name => data)
+        # Only add result if the timeout is above the configured threshold
+        if (Time.now - result.created_at) < SimpleCov.merge_timeout
+          results << result
+        end
+      end
+      results
+    end
+
+    def merged_result
+      merged = {}
+      results = all_results
+      results.each do |result|
+        merged = result.original_result.merge_resultset(merged)
+      end
+      result = SimpleCov::Result.new(merged)
+      # Specify the command name
+      result.command_name = results.map(&:command_name).sort.join(", ")
+      result
+    end
+  end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 3638dcbb2d3568b26e7b323a31e8d05b78a631ce..644f767402c33153981d18a4bae2f0b05dd5a0c7 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,6 +1,14 @@
 if ENV['SIMPLECOV']
   require 'simplecov'
-  SimpleCov.start :rails
+  require 'simplecov-rcov'
+
+  SimpleCov.start :rails do
+    if ENV['CI_BUILD_NAME']
+      coverage_dir "coverage/#{ENV['CI_BUILD_NAME']}"
+      command_name ENV['CI_BUILD_NAME']
+      merge_timeout 7200
+    end
+  end
 end
 
 ENV["RAILS_ENV"] ||= 'test'