diff --git a/changelogs/unreleased/28080-system-checks.yml b/changelogs/unreleased/28080-system-checks.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7d83014279aa48813ae1ad2284a80a69988d5127
--- /dev/null
+++ b/changelogs/unreleased/28080-system-checks.yml
@@ -0,0 +1,4 @@
+---
+title: Refactored gitlab:app:check into SystemCheck liberary and improve some checks
+merge_request: 9173
+author:
diff --git a/lib/system_check.rb b/lib/system_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..466c39904fa6a4435fbdf27eb8fa1ae681245d12
--- /dev/null
+++ b/lib/system_check.rb
@@ -0,0 +1,21 @@
+# Library to perform System Checks
+#
+# Every Check is implemented as its own class inherited from SystemCheck::BaseCheck
+# Execution coordination and boilerplate output is done by the SystemCheck::SimpleExecutor
+#
+# This structure decouples checks from Rake tasks and facilitates unit-testing
+module SystemCheck
+  # Executes a bunch of checks for specified component
+  #
+  # @param [String] component name of the component relative to the checks being executed
+  # @param [Array<BaseCheck>] checks classes of corresponding checks to be executed in the same order
+  def self.run(component, checks = [])
+    executor = SimpleExecutor.new(component)
+
+    checks.each do |check|
+      executor << check
+    end
+
+    executor.execute
+  end
+end
diff --git a/lib/system_check/app/active_users_check.rb b/lib/system_check/app/active_users_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1d72c8d69034cc469d8401a7b3a74fc08a589ee8
--- /dev/null
+++ b/lib/system_check/app/active_users_check.rb
@@ -0,0 +1,17 @@
+module SystemCheck
+  module App
+    class ActiveUsersCheck < SystemCheck::BaseCheck
+      set_name 'Active users:'
+
+      def multi_check
+        active_users = User.active.count
+
+        if active_users > 0
+          $stdout.puts active_users.to_s.color(:green)
+        else
+          $stdout.puts active_users.to_s.color(:red)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/database_config_exists_check.rb b/lib/system_check/app/database_config_exists_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d1fae192350e49d14336f64c0060fc6de42330ee
--- /dev/null
+++ b/lib/system_check/app/database_config_exists_check.rb
@@ -0,0 +1,25 @@
+module SystemCheck
+  module App
+    class DatabaseConfigExistsCheck < SystemCheck::BaseCheck
+      set_name 'Database config exists?'
+
+      def check?
+        database_config_file = Rails.root.join('config', 'database.yml')
+
+        File.exist?(database_config_file)
+      end
+
+      def show_error
+        try_fixing_it(
+          'Copy config/database.yml.<your db> to config/database.yml',
+          'Check that the information in config/database.yml is correct'
+        )
+        for_more_information(
+          'doc/install/databases.md',
+          'http://guides.rubyonrails.org/getting_started.html#configuring-a-database'
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/git_config_check.rb b/lib/system_check/app/git_config_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..198867f7ac65f96547be59fbbd786bf4b6ab5422
--- /dev/null
+++ b/lib/system_check/app/git_config_check.rb
@@ -0,0 +1,42 @@
+module SystemCheck
+  module App
+    class GitConfigCheck < SystemCheck::BaseCheck
+      OPTIONS = {
+        'core.autocrlf' => 'input'
+      }.freeze
+
+      set_name 'Git configured correctly?'
+
+      def check?
+        correct_options = OPTIONS.map do |name, value|
+          run_command(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value
+        end
+
+        correct_options.all?
+      end
+
+      # Tries to configure git itself
+      #
+      # Returns true if all subcommands were successful (according to their exit code)
+      # Returns false if any or all subcommands failed.
+      def repair!
+        return false unless is_gitlab_user?
+
+        command_success = OPTIONS.map do |name, value|
+          system(*%W(#{Gitlab.config.git.bin_path} config --global #{name} #{value}))
+        end
+
+        command_success.all?
+      end
+
+      def show_error
+        try_fixing_it(
+          sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{OPTIONS['core.autocrlf']}\"")
+        )
+        for_more_information(
+          see_installation_guide_section 'GitLab'
+        )
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/git_version_check.rb b/lib/system_check/app/git_version_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c388682dfb4e0b4144351a8ade0edf86fc1b222d
--- /dev/null
+++ b/lib/system_check/app/git_version_check.rb
@@ -0,0 +1,29 @@
+module SystemCheck
+  module App
+    class GitVersionCheck < SystemCheck::BaseCheck
+      set_name -> { "Git version >= #{self.required_version} ?" }
+      set_check_pass -> { "yes (#{self.current_version})" }
+
+      def self.required_version
+        @required_version ||= Gitlab::VersionInfo.new(2, 7, 3)
+      end
+
+      def self.current_version
+        @current_version ||= Gitlab::VersionInfo.parse(run_command(%W(#{Gitlab.config.git.bin_path} --version)))
+      end
+
+      def check?
+        self.class.current_version.valid? && self.class.required_version <= self.class.current_version
+      end
+
+      def show_error
+        $stdout.puts "Your git bin path is \"#{Gitlab.config.git.bin_path}\""
+
+        try_fixing_it(
+          "Update your git to a version >= #{self.class.required_version} from #{self.class.current_version}"
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/gitlab_config_exists_check.rb b/lib/system_check/app/gitlab_config_exists_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..247aa0994e4f1471c67cfb901d6352bf661ed417
--- /dev/null
+++ b/lib/system_check/app/gitlab_config_exists_check.rb
@@ -0,0 +1,24 @@
+module SystemCheck
+  module App
+    class GitlabConfigExistsCheck < SystemCheck::BaseCheck
+      set_name 'GitLab config exists?'
+
+      def check?
+        gitlab_config_file = Rails.root.join('config', 'gitlab.yml')
+
+        File.exist?(gitlab_config_file)
+      end
+
+      def show_error
+        try_fixing_it(
+          'Copy config/gitlab.yml.example to config/gitlab.yml',
+          'Update config/gitlab.yml to match your setup'
+        )
+        for_more_information(
+          see_installation_guide_section 'GitLab'
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/gitlab_config_up_to_date_check.rb b/lib/system_check/app/gitlab_config_up_to_date_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c609e48e133655ff137abc94490de322a7de5457
--- /dev/null
+++ b/lib/system_check/app/gitlab_config_up_to_date_check.rb
@@ -0,0 +1,30 @@
+module SystemCheck
+  module App
+    class GitlabConfigUpToDateCheck < SystemCheck::BaseCheck
+      set_name 'GitLab config up to date?'
+      set_skip_reason "can't check because of previous errors"
+
+      def skip?
+        gitlab_config_file = Rails.root.join('config', 'gitlab.yml')
+        !File.exist?(gitlab_config_file)
+      end
+
+      def check?
+        # omniauth or ldap could have been deleted from the file
+        !Gitlab.config['git_host']
+      end
+
+      def show_error
+        try_fixing_it(
+          'Back-up your config/gitlab.yml',
+          'Copy config/gitlab.yml.example to config/gitlab.yml',
+          'Update config/gitlab.yml to match your setup'
+        )
+        for_more_information(
+          see_installation_guide_section 'GitLab'
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/init_script_exists_check.rb b/lib/system_check/app/init_script_exists_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d246e058e86d959122e309e2b3c9a6a237fa45e8
--- /dev/null
+++ b/lib/system_check/app/init_script_exists_check.rb
@@ -0,0 +1,27 @@
+module SystemCheck
+  module App
+    class InitScriptExistsCheck < SystemCheck::BaseCheck
+      set_name 'Init script exists?'
+      set_skip_reason 'skipped (omnibus-gitlab has no init script)'
+
+      def skip?
+        omnibus_gitlab?
+      end
+
+      def check?
+        script_path = '/etc/init.d/gitlab'
+        File.exist?(script_path)
+      end
+
+      def show_error
+        try_fixing_it(
+          'Install the init script'
+        )
+        for_more_information(
+          see_installation_guide_section 'Install Init Script'
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/init_script_up_to_date_check.rb b/lib/system_check/app/init_script_up_to_date_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..015c7ed1731fbe4336ca1626fb440bfb90313173
--- /dev/null
+++ b/lib/system_check/app/init_script_up_to_date_check.rb
@@ -0,0 +1,43 @@
+module SystemCheck
+  module App
+    class InitScriptUpToDateCheck < SystemCheck::BaseCheck
+      SCRIPT_PATH = '/etc/init.d/gitlab'.freeze
+
+      set_name 'Init script up-to-date?'
+      set_skip_reason 'skipped (omnibus-gitlab has no init script)'
+
+      def skip?
+        omnibus_gitlab?
+      end
+
+      def multi_check
+        recipe_path = Rails.root.join('lib/support/init.d/', 'gitlab')
+
+        unless File.exist?(SCRIPT_PATH)
+          $stdout.puts "can't check because of previous errors".color(:magenta)
+          return
+        end
+
+        recipe_content = File.read(recipe_path)
+        script_content = File.read(SCRIPT_PATH)
+
+        if recipe_content == script_content
+          $stdout.puts 'yes'.color(:green)
+        else
+          $stdout.puts 'no'.color(:red)
+          show_error
+        end
+      end
+
+      def show_error
+        try_fixing_it(
+          'Re-download the init script'
+        )
+        for_more_information(
+          see_installation_guide_section 'Install Init Script'
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/log_writable_check.rb b/lib/system_check/app/log_writable_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3e0c436d6eecada871e8ba721321791614c80f7a
--- /dev/null
+++ b/lib/system_check/app/log_writable_check.rb
@@ -0,0 +1,28 @@
+module SystemCheck
+  module App
+    class LogWritableCheck < SystemCheck::BaseCheck
+      set_name 'Log directory writable?'
+
+      def check?
+        File.writable?(log_path)
+      end
+
+      def show_error
+        try_fixing_it(
+          "sudo chown -R gitlab #{log_path}",
+          "sudo chmod -R u+rwX #{log_path}"
+        )
+        for_more_information(
+          see_installation_guide_section 'GitLab'
+        )
+        fix_and_rerun
+      end
+
+      private
+
+      def log_path
+        Rails.root.join('log')
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/migrations_are_up_check.rb b/lib/system_check/app/migrations_are_up_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5eedbacce77cd9e84769d47a849ae3ed45677944
--- /dev/null
+++ b/lib/system_check/app/migrations_are_up_check.rb
@@ -0,0 +1,20 @@
+module SystemCheck
+  module App
+    class MigrationsAreUpCheck < SystemCheck::BaseCheck
+      set_name 'All migrations up?'
+
+      def check?
+        migration_status, _ = Gitlab::Popen.popen(%w(bundle exec rake db:migrate:status))
+
+        migration_status !~ /down\s+\d{14}/
+      end
+
+      def show_error
+        try_fixing_it(
+          sudo_gitlab('bundle exec rake db:migrate RAILS_ENV=production')
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/orphaned_group_members_check.rb b/lib/system_check/app/orphaned_group_members_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2b46d36fe517440a77802ac9bc9ed2f13db498db
--- /dev/null
+++ b/lib/system_check/app/orphaned_group_members_check.rb
@@ -0,0 +1,20 @@
+module SystemCheck
+  module App
+    class OrphanedGroupMembersCheck < SystemCheck::BaseCheck
+      set_name 'Database contains orphaned GroupMembers?'
+      set_check_pass 'no'
+      set_check_fail 'yes'
+
+      def check?
+        !GroupMember.where('user_id not in (select id from users)').exists?
+      end
+
+      def show_error
+        try_fixing_it(
+          'You can delete the orphaned records using something along the lines of:',
+          sudo_gitlab("bundle exec rails runner -e production 'GroupMember.where(\"user_id NOT IN (SELECT id FROM users)\").delete_all'")
+        )
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/projects_have_namespace_check.rb b/lib/system_check/app/projects_have_namespace_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a6ec9f7665c7871731196644d25ba0d5a488768f
--- /dev/null
+++ b/lib/system_check/app/projects_have_namespace_check.rb
@@ -0,0 +1,37 @@
+module SystemCheck
+  module App
+    class ProjectsHaveNamespaceCheck < SystemCheck::BaseCheck
+      set_name 'Projects have namespace:'
+      set_skip_reason "can't check, you have no projects"
+
+      def skip?
+        !Project.exists?
+      end
+
+      def multi_check
+        $stdout.puts ''
+
+        Project.find_each(batch_size: 100) do |project|
+          $stdout.print sanitized_message(project)
+
+          if project.namespace
+            $stdout.puts 'yes'.color(:green)
+          else
+            $stdout.puts 'no'.color(:red)
+            show_error
+          end
+        end
+      end
+
+      def show_error
+        try_fixing_it(
+          "Migrate global projects"
+        )
+        for_more_information(
+          "doc/update/5.4-to-6.0.md in section \"#global-projects\""
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/redis_version_check.rb b/lib/system_check/app/redis_version_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a0610e73576cd1b9ccc4bcfe99a94eb89c870f1d
--- /dev/null
+++ b/lib/system_check/app/redis_version_check.rb
@@ -0,0 +1,25 @@
+module SystemCheck
+  module App
+    class RedisVersionCheck < SystemCheck::BaseCheck
+      MIN_REDIS_VERSION = '2.8.0'.freeze
+      set_name "Redis version >= #{MIN_REDIS_VERSION}?"
+
+      def check?
+        redis_version = run_command(%w(redis-cli --version))
+        redis_version = redis_version.try(:match, /redis-cli (\d+\.\d+\.\d+)/)
+
+        redis_version && (Gem::Version.new(redis_version[1]) > Gem::Version.new(MIN_REDIS_VERSION))
+      end
+
+      def show_error
+        try_fixing_it(
+          "Update your redis server to a version >= #{MIN_REDIS_VERSION}"
+        )
+        for_more_information(
+          'gitlab-public-wiki/wiki/Trouble-Shooting-Guide in section sidekiq'
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/ruby_version_check.rb b/lib/system_check/app/ruby_version_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fd82f5f8a4a43b3b43909051b6f8413c35c73b8b
--- /dev/null
+++ b/lib/system_check/app/ruby_version_check.rb
@@ -0,0 +1,27 @@
+module SystemCheck
+  module App
+    class RubyVersionCheck < SystemCheck::BaseCheck
+      set_name -> { "Ruby version >= #{self.required_version} ?" }
+      set_check_pass -> { "yes (#{self.current_version})" }
+
+      def self.required_version
+        @required_version ||= Gitlab::VersionInfo.new(2, 3, 3)
+      end
+
+      def self.current_version
+        @current_version ||= Gitlab::VersionInfo.parse(run_command(%w(ruby --version)))
+      end
+
+      def check?
+        self.class.current_version.valid? && self.class.required_version <= self.class.current_version
+      end
+
+      def show_error
+        try_fixing_it(
+          "Update your ruby to a version >= #{self.class.required_version} from #{self.class.current_version}"
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/tmp_writable_check.rb b/lib/system_check/app/tmp_writable_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..99a75e57abf3cc25a8eef038f6cbe764ebd006aa
--- /dev/null
+++ b/lib/system_check/app/tmp_writable_check.rb
@@ -0,0 +1,28 @@
+module SystemCheck
+  module App
+    class TmpWritableCheck < SystemCheck::BaseCheck
+      set_name 'Tmp directory writable?'
+
+      def check?
+        File.writable?(tmp_path)
+      end
+
+      def show_error
+        try_fixing_it(
+          "sudo chown -R gitlab #{tmp_path}",
+          "sudo chmod -R u+rwX #{tmp_path}"
+        )
+        for_more_information(
+          see_installation_guide_section 'GitLab'
+        )
+        fix_and_rerun
+      end
+
+      private
+
+      def tmp_path
+        Rails.root.join('tmp')
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/uploads_directory_exists_check.rb b/lib/system_check/app/uploads_directory_exists_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7026d0ba0755a4a4d7b06421a07f8371c65dc3b0
--- /dev/null
+++ b/lib/system_check/app/uploads_directory_exists_check.rb
@@ -0,0 +1,21 @@
+module SystemCheck
+  module App
+    class UploadsDirectoryExistsCheck < SystemCheck::BaseCheck
+      set_name 'Uploads directory exists?'
+
+      def check?
+        File.directory?(Rails.root.join('public/uploads'))
+      end
+
+      def show_error
+        try_fixing_it(
+          "sudo -u #{gitlab_user} mkdir #{Rails.root}/public/uploads"
+        )
+        for_more_information(
+          see_installation_guide_section 'GitLab'
+        )
+        fix_and_rerun
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/uploads_path_permission_check.rb b/lib/system_check/app/uploads_path_permission_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7df6c060254ef4d25dc1d2cc506c596e361139f3
--- /dev/null
+++ b/lib/system_check/app/uploads_path_permission_check.rb
@@ -0,0 +1,36 @@
+module SystemCheck
+  module App
+    class UploadsPathPermissionCheck < SystemCheck::BaseCheck
+      set_name 'Uploads directory has correct permissions?'
+      set_skip_reason 'skipped (no uploads folder found)'
+
+      def skip?
+        !File.directory?(rails_uploads_path)
+      end
+
+      def check?
+        File.stat(uploads_fullpath).mode == 040700
+      end
+
+      def show_error
+        try_fixing_it(
+          "sudo chmod 700 #{uploads_fullpath}"
+        )
+        for_more_information(
+          see_installation_guide_section 'GitLab'
+        )
+        fix_and_rerun
+      end
+
+      private
+
+      def rails_uploads_path
+        Rails.root.join('public/uploads')
+      end
+
+      def uploads_fullpath
+        File.realpath(rails_uploads_path)
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/uploads_path_tmp_permission_check.rb b/lib/system_check/app/uploads_path_tmp_permission_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b276a81eac119bb72cff84c0a473ccbfb135415b
--- /dev/null
+++ b/lib/system_check/app/uploads_path_tmp_permission_check.rb
@@ -0,0 +1,40 @@
+module SystemCheck
+  module App
+    class UploadsPathTmpPermissionCheck < SystemCheck::BaseCheck
+      set_name 'Uploads directory tmp has correct permissions?'
+      set_skip_reason 'skipped (no tmp uploads folder yet)'
+
+      def skip?
+        !File.directory?(uploads_fullpath) || !Dir.exist?(upload_path_tmp)
+      end
+
+      def check?
+        # If tmp upload dir has incorrect permissions, assume others do as well
+        # Verify drwx------ permissions
+        File.stat(upload_path_tmp).mode == 040700 && File.owned?(upload_path_tmp)
+      end
+
+      def show_error
+        try_fixing_it(
+          "sudo chown -R #{gitlab_user} #{uploads_fullpath}",
+          "sudo find #{uploads_fullpath} -type f -exec chmod 0644 {} \\;",
+          "sudo find #{uploads_fullpath} -type d -not -path #{uploads_fullpath} -exec chmod 0700 {} \\;"
+        )
+        for_more_information(
+          see_installation_guide_section 'GitLab'
+        )
+        fix_and_rerun
+      end
+
+      private
+
+      def upload_path_tmp
+        File.join(uploads_fullpath, 'tmp')
+      end
+
+      def uploads_fullpath
+        File.realpath(Rails.root.join('public/uploads'))
+      end
+    end
+  end
+end
diff --git a/lib/system_check/base_check.rb b/lib/system_check/base_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5dcb3f0886bf5f5108e3e5c1d1696ce74a3970e8
--- /dev/null
+++ b/lib/system_check/base_check.rb
@@ -0,0 +1,129 @@
+module SystemCheck
+  # Base class for Checks. You must inherit from here
+  # and implement the methods below when necessary
+  class BaseCheck
+    include ::SystemCheck::Helpers
+
+    # Define a custom term for when check passed
+    #
+    # @param [String] term used when check passed (default: 'yes')
+    def self.set_check_pass(term)
+      @check_pass = term
+    end
+
+    # Define a custom term for when check failed
+    #
+    # @param [String] term used when check failed (default: 'no')
+    def self.set_check_fail(term)
+      @check_fail = term
+    end
+
+    # Define the name of the SystemCheck that will be displayed during execution
+    #
+    # @param [String] name of the check
+    def self.set_name(name)
+      @name = name
+    end
+
+    # Define the reason why we skipped the SystemCheck
+    #
+    # This is only used if subclass implements `#skip?`
+    #
+    # @param [String] reason to be displayed
+    def self.set_skip_reason(reason)
+      @skip_reason = reason
+    end
+
+    # Term to be displayed when check passed
+    #
+    # @return [String] term when check passed ('yes' if not re-defined in a subclass)
+    def self.check_pass
+      call_or_return(@check_pass) || 'yes'
+    end
+
+    ## Term to be displayed when check failed
+    #
+    # @return [String] term when check failed ('no' if not re-defined in a subclass)
+    def self.check_fail
+      call_or_return(@check_fail) || 'no'
+    end
+
+    # Name of the SystemCheck defined by the subclass
+    #
+    # @return [String] the name
+    def self.display_name
+      call_or_return(@name) || self.name
+    end
+
+    # Skip reason defined by the subclass
+    #
+    # @return [String] the reason
+    def self.skip_reason
+      call_or_return(@skip_reason) || 'skipped'
+    end
+
+    # Does the check support automatically repair routine?
+    #
+    # @return [Boolean] whether check implemented `#repair!` method or not
+    def can_repair?
+      self.class.instance_methods(false).include?(:repair!)
+    end
+
+    def can_skip?
+      self.class.instance_methods(false).include?(:skip?)
+    end
+
+    def is_multi_check?
+      self.class.instance_methods(false).include?(:multi_check)
+    end
+
+    # Execute the check routine
+    #
+    # This is where you should implement the main logic that will return
+    # a boolean at the end
+    #
+    # You should not print any output to STDOUT here, use the specific methods instead
+    #
+    # @return [Boolean] whether check passed or failed
+    def check?
+      raise NotImplementedError
+    end
+
+    # Execute a custom check that cover multiple unities
+    #
+    # When using multi_check you have to provide the output yourself
+    def multi_check
+      raise NotImplementedError
+    end
+
+    # Prints troubleshooting instructions
+    #
+    # This is where you should print detailed information for any error found during #check?
+    #
+    # You may use helper methods to help format the output:
+    #
+    # @see #try_fixing_it
+    # @see #fix_and_rerun
+    # @see #for_more_infromation
+    def show_error
+      raise NotImplementedError
+    end
+
+    # When implemented by a subclass, will attempt to fix the issue automatically
+    def repair!
+      raise NotImplementedError
+    end
+
+    # When implemented by a subclass, will evaluate whether check should be skipped or not
+    #
+    # @return [Boolean] whether or not this check should be skipped
+    def skip?
+      raise NotImplementedError
+    end
+
+    def self.call_or_return(input)
+      input.respond_to?(:call) ? input.call : input
+    end
+    private_class_method :call_or_return
+  end
+end
diff --git a/lib/system_check/helpers.rb b/lib/system_check/helpers.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c42ae4fe4c49f41d428079e17c89f3337cef376c
--- /dev/null
+++ b/lib/system_check/helpers.rb
@@ -0,0 +1,75 @@
+require 'tasks/gitlab/task_helpers'
+
+module SystemCheck
+  module Helpers
+    include ::Gitlab::TaskHelpers
+
+    # Display a message telling to fix and rerun the checks
+    def fix_and_rerun
+      $stdout.puts '  Please fix the error above and rerun the checks.'.color(:red)
+    end
+
+    # Display a formatted list of references (documentation or links) where to find more information
+    #
+    # @param [Array<String>] sources one or more references (documentation or links)
+    def for_more_information(*sources)
+      $stdout.puts '  For more information see:'.color(:blue)
+      sources.each do |source|
+        $stdout.puts "  #{source}"
+      end
+    end
+
+    def see_installation_guide_section(section)
+      "doc/install/installation.md in section \"#{section}\""
+    end
+
+    # @deprecated This will no longer be used when all checks were executed using SystemCheck
+    def finished_checking(component)
+      $stdout.puts ''
+      $stdout.puts "Checking #{component.color(:yellow)} ... #{'Finished'.color(:green)}"
+      $stdout.puts ''
+    end
+
+    # @deprecated This will no longer be used when all checks were executed using SystemCheck
+    def start_checking(component)
+      $stdout.puts "Checking #{component.color(:yellow)} ..."
+      $stdout.puts ''
+    end
+
+    # Display a formatted list of instructions on how to fix the issue identified by the #check?
+    #
+    # @param [Array<String>] steps one or short sentences with help how to fix the issue
+    def try_fixing_it(*steps)
+      steps = steps.shift if steps.first.is_a?(Array)
+
+      $stdout.puts '  Try fixing it:'.color(:blue)
+      steps.each do |step|
+        $stdout.puts "  #{step}"
+      end
+    end
+
+    def sanitized_message(project)
+      if should_sanitize?
+        "#{project.namespace_id.to_s.color(:yellow)}/#{project.id.to_s.color(:yellow)} ... "
+      else
+        "#{project.name_with_namespace.color(:yellow)} ... "
+      end
+    end
+
+    def should_sanitize?
+      if ENV['SANITIZE'] == 'true'
+        true
+      else
+        false
+      end
+    end
+
+    def omnibus_gitlab?
+      Dir.pwd == '/opt/gitlab/embedded/service/gitlab-rails'
+    end
+
+    def sudo_gitlab(command)
+      "sudo -u #{gitlab_user} -H #{command}"
+    end
+  end
+end
diff --git a/lib/system_check/simple_executor.rb b/lib/system_check/simple_executor.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dc2d4643a011417f82bf80c409945c0f89035052
--- /dev/null
+++ b/lib/system_check/simple_executor.rb
@@ -0,0 +1,99 @@
+module SystemCheck
+  # Simple Executor is current default executor for GitLab
+  # It is a simple port from display logic in the old check.rake
+  #
+  # There is no concurrency level and the output is progressively
+  # printed into the STDOUT
+  #
+  # @attr_reader [Array<BaseCheck>] checks classes of corresponding checks to be executed in the same order
+  # @attr_reader [String] component name of the component relative to the checks being executed
+  class SimpleExecutor
+    attr_reader :checks
+    attr_reader :component
+
+    # @param [String] component name of the component relative to the checks being executed
+    def initialize(component)
+      raise ArgumentError unless component.is_a? String
+
+      @component = component
+      @checks = Set.new
+    end
+
+    # Add a check to be executed
+    #
+    # @param [BaseCheck] check class
+    def <<(check)
+      raise ArgumentError unless check < BaseCheck
+      @checks << check
+    end
+
+    # Executes defined checks in the specified order and outputs confirmation or error information
+    def execute
+      start_checking(component)
+
+      @checks.each do |check|
+        run_check(check)
+      end
+
+      finished_checking(component)
+    end
+
+    # Executes a single check
+    #
+    # @param [SystemCheck::BaseCheck] check_klass
+    def run_check(check_klass)
+      $stdout.print "#{check_klass.display_name} ... "
+
+      check = check_klass.new
+
+      # When implements skip method, we run it first, and if true, skip the check
+      if check.can_skip? && check.skip?
+        $stdout.puts check_klass.skip_reason.color(:magenta)
+        return
+      end
+
+      # When implements a multi check, we don't control the output
+      if check.is_multi_check?
+        check.multi_check
+        return
+      end
+
+      if check.check?
+        $stdout.puts check_klass.check_pass.color(:green)
+      else
+        $stdout.puts check_klass.check_fail.color(:red)
+
+        if check.can_repair?
+          $stdout.print 'Trying to fix error automatically. ...'
+          if check.repair!
+            $stdout.puts 'Success'.color(:green)
+            return
+          else
+            $stdout.puts 'Failed'.color(:red)
+          end
+        end
+
+        check.show_error
+      end
+    end
+
+    private
+
+    # Prints header content for the series of checks to be executed for this component
+    #
+    # @param [String] component name of the component relative to the checks being executed
+    def start_checking(component)
+      $stdout.puts "Checking #{component.color(:yellow)} ..."
+      $stdout.puts ''
+    end
+
+    # Prints footer content for the series of checks executed for this component
+    #
+    # @param [String] component name of the component relative to the checks being executed
+    def finished_checking(component)
+      $stdout.puts ''
+      $stdout.puts "Checking #{component.color(:yellow)} ... #{'Finished'.color(:green)}"
+      $stdout.puts ''
+    end
+  end
+end
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index f41c73154f506ce3695ef981aee04ec0a5264790..63c5e9b9c83c28882d00b59b14cb87b388ab78f9 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -1,5 +1,9 @@
+# Temporary hack, until we migrate all checks to SystemCheck format
+require 'system_check'
+require 'system_check/helpers'
+
 namespace :gitlab do
-  desc "GitLab | Check the configuration of GitLab and its environment"
+  desc 'GitLab | Check the configuration of GitLab and its environment'
   task check: %w{gitlab:gitlab_shell:check
                  gitlab:sidekiq:check
                  gitlab:incoming_email:check
@@ -7,331 +11,38 @@ namespace :gitlab do
                  gitlab:app:check}
 
   namespace :app do
-    desc "GitLab | Check the configuration of the GitLab Rails app"
+    desc 'GitLab | Check the configuration of the GitLab Rails app'
     task check: :environment  do
       warn_user_is_not_gitlab
-      start_checking "GitLab"
-
-      check_git_config
-      check_database_config_exists
-      check_migrations_are_up
-      check_orphaned_group_members
-      check_gitlab_config_exists
-      check_gitlab_config_not_outdated
-      check_log_writable
-      check_tmp_writable
-      check_uploads
-      check_init_script_exists
-      check_init_script_up_to_date
-      check_projects_have_namespace
-      check_redis_version
-      check_ruby_version
-      check_git_version
-      check_active_users
-
-      finished_checking "GitLab"
-    end
-
-    # Checks
-    ########################
-
-    def check_git_config
-      print "Git configured with autocrlf=input? ... "
-
-      options = {
-        "core.autocrlf" => "input"
-      }
-
-      correct_options = options.map do |name, value|
-        run_command(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value
-      end
-
-      if correct_options.all?
-        puts "yes".color(:green)
-      else
-        print "Trying to fix Git error automatically. ..."
-
-        if auto_fix_git_config(options)
-          puts "Success".color(:green)
-        else
-          puts "Failed".color(:red)
-          try_fixing_it(
-            sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{options["core.autocrlf"]}\"")
-          )
-          for_more_information(
-            see_installation_guide_section "GitLab"
-          )
-        end
-      end
-    end
-
-    def check_database_config_exists
-      print "Database config exists? ... "
-
-      database_config_file = Rails.root.join("config", "database.yml")
-
-      if File.exist?(database_config_file)
-        puts "yes".color(:green)
-      else
-        puts "no".color(:red)
-        try_fixing_it(
-          "Copy config/database.yml.<your db> to config/database.yml",
-          "Check that the information in config/database.yml is correct"
-        )
-        for_more_information(
-          see_database_guide,
-          "http://guides.rubyonrails.org/getting_started.html#configuring-a-database"
-        )
-        fix_and_rerun
-      end
-    end
-
-    def check_gitlab_config_exists
-      print "GitLab config exists? ... "
-
-      gitlab_config_file = Rails.root.join("config", "gitlab.yml")
-
-      if File.exist?(gitlab_config_file)
-        puts "yes".color(:green)
-      else
-        puts "no".color(:red)
-        try_fixing_it(
-          "Copy config/gitlab.yml.example to config/gitlab.yml",
-          "Update config/gitlab.yml to match your setup"
-        )
-        for_more_information(
-          see_installation_guide_section "GitLab"
-        )
-        fix_and_rerun
-      end
-    end
-
-    def check_gitlab_config_not_outdated
-      print "GitLab config outdated? ... "
-
-      gitlab_config_file = Rails.root.join("config", "gitlab.yml")
-      unless File.exist?(gitlab_config_file)
-        puts "can't check because of previous errors".color(:magenta)
-      end
-
-      # omniauth or ldap could have been deleted from the file
-      unless Gitlab.config['git_host']
-        puts "no".color(:green)
-      else
-        puts "yes".color(:red)
-        try_fixing_it(
-          "Backup your config/gitlab.yml",
-          "Copy config/gitlab.yml.example to config/gitlab.yml",
-          "Update config/gitlab.yml to match your setup"
-        )
-        for_more_information(
-          see_installation_guide_section "GitLab"
-        )
-        fix_and_rerun
-      end
-    end
-
-    def check_init_script_exists
-      print "Init script exists? ... "
-
-      if omnibus_gitlab?
-        puts 'skipped (omnibus-gitlab has no init script)'.color(:magenta)
-        return
-      end
-
-      script_path = "/etc/init.d/gitlab"
-
-      if File.exist?(script_path)
-        puts "yes".color(:green)
-      else
-        puts "no".color(:red)
-        try_fixing_it(
-          "Install the init script"
-        )
-        for_more_information(
-          see_installation_guide_section "Install Init Script"
-        )
-        fix_and_rerun
-      end
-    end
-
-    def check_init_script_up_to_date
-      print "Init script up-to-date? ... "
-
-      if omnibus_gitlab?
-        puts 'skipped (omnibus-gitlab has no init script)'.color(:magenta)
-        return
-      end
-
-      recipe_path = Rails.root.join("lib/support/init.d/", "gitlab")
-      script_path = "/etc/init.d/gitlab"
-
-      unless File.exist?(script_path)
-        puts "can't check because of previous errors".color(:magenta)
-        return
-      end
-
-      recipe_content = File.read(recipe_path)
-      script_content = File.read(script_path)
-
-      if recipe_content == script_content
-        puts "yes".color(:green)
-      else
-        puts "no".color(:red)
-        try_fixing_it(
-          "Redownload the init script"
-        )
-        for_more_information(
-          see_installation_guide_section "Install Init Script"
-        )
-        fix_and_rerun
-      end
-    end
-
-    def check_migrations_are_up
-      print "All migrations up? ... "
-
-      migration_status, _ = Gitlab::Popen.popen(%w(bundle exec rake db:migrate:status))
-
-      unless migration_status =~ /down\s+\d{14}/
-        puts "yes".color(:green)
-      else
-        puts "no".color(:red)
-        try_fixing_it(
-          sudo_gitlab("bundle exec rake db:migrate RAILS_ENV=production")
-        )
-        fix_and_rerun
-      end
-    end
-
-    def check_orphaned_group_members
-      print "Database contains orphaned GroupMembers? ... "
-      if GroupMember.where("user_id not in (select id from users)").count > 0
-        puts "yes".color(:red)
-        try_fixing_it(
-          "You can delete the orphaned records using something along the lines of:",
-          sudo_gitlab("bundle exec rails runner -e production 'GroupMember.where(\"user_id NOT IN (SELECT id FROM users)\").delete_all'")
-        )
-      else
-        puts "no".color(:green)
-      end
-    end
-
-    def check_log_writable
-      print "Log directory writable? ... "
-
-      log_path = Rails.root.join("log")
-
-      if File.writable?(log_path)
-        puts "yes".color(:green)
-      else
-        puts "no".color(:red)
-        try_fixing_it(
-          "sudo chown -R gitlab #{log_path}",
-          "sudo chmod -R u+rwX #{log_path}"
-        )
-        for_more_information(
-          see_installation_guide_section "GitLab"
-        )
-        fix_and_rerun
-      end
-    end
 
-    def check_tmp_writable
-      print "Tmp directory writable? ... "
-
-      tmp_path = Rails.root.join("tmp")
-
-      if File.writable?(tmp_path)
-        puts "yes".color(:green)
-      else
-        puts "no".color(:red)
-        try_fixing_it(
-          "sudo chown -R gitlab #{tmp_path}",
-          "sudo chmod -R u+rwX #{tmp_path}"
-        )
-        for_more_information(
-          see_installation_guide_section "GitLab"
-        )
-        fix_and_rerun
-      end
-    end
-
-    def check_uploads
-      print "Uploads directory setup correctly? ... "
-
-      unless File.directory?(Rails.root.join('public/uploads'))
-        puts "no".color(:red)
-        try_fixing_it(
-          "sudo -u #{gitlab_user} mkdir #{Rails.root}/public/uploads"
-        )
-        for_more_information(
-          see_installation_guide_section "GitLab"
-        )
-        fix_and_rerun
-        return
-      end
-
-      upload_path = File.realpath(Rails.root.join('public/uploads'))
-      upload_path_tmp = File.join(upload_path, 'tmp')
-
-      if File.stat(upload_path).mode == 040700
-        unless Dir.exist?(upload_path_tmp)
-          puts 'skipped (no tmp uploads folder yet)'.color(:magenta)
-          return
-        end
-
-        # If tmp upload dir has incorrect permissions, assume others do as well
-        # Verify drwx------ permissions
-        if File.stat(upload_path_tmp).mode == 040700 && File.owned?(upload_path_tmp)
-          puts "yes".color(:green)
-        else
-          puts "no".color(:red)
-          try_fixing_it(
-            "sudo chown -R #{gitlab_user} #{upload_path}",
-            "sudo find #{upload_path} -type f -exec chmod 0644 {} \\;",
-            "sudo find #{upload_path} -type d -not -path #{upload_path} -exec chmod 0700 {} \\;"
-          )
-          for_more_information(
-            see_installation_guide_section "GitLab"
-          )
-          fix_and_rerun
-        end
-      else
-        puts "no".color(:red)
-        try_fixing_it(
-          "sudo chmod 700 #{upload_path}"
-        )
-        for_more_information(
-          see_installation_guide_section "GitLab"
-        )
-        fix_and_rerun
-      end
-    end
-
-    def check_redis_version
-      min_redis_version = "2.8.0"
-      print "Redis version >= #{min_redis_version}? ... "
-
-      redis_version = run_command(%w(redis-cli --version))
-      redis_version = redis_version.try(:match, /redis-cli (\d+\.\d+\.\d+)/)
-      if redis_version &&
-          (Gem::Version.new(redis_version[1]) > Gem::Version.new(min_redis_version))
-        puts "yes".color(:green)
-      else
-        puts "no".color(:red)
-        try_fixing_it(
-          "Update your redis server to a version >= #{min_redis_version}"
-        )
-        for_more_information(
-          "gitlab-public-wiki/wiki/Trouble-Shooting-Guide in section sidekiq"
-        )
-        fix_and_rerun
-      end
+      checks = [
+        SystemCheck::App::GitConfigCheck,
+        SystemCheck::App::DatabaseConfigExistsCheck,
+        SystemCheck::App::MigrationsAreUpCheck,
+        SystemCheck::App::OrphanedGroupMembersCheck,
+        SystemCheck::App::GitlabConfigExistsCheck,
+        SystemCheck::App::GitlabConfigUpToDateCheck,
+        SystemCheck::App::LogWritableCheck,
+        SystemCheck::App::TmpWritableCheck,
+        SystemCheck::App::UploadsDirectoryExistsCheck,
+        SystemCheck::App::UploadsPathPermissionCheck,
+        SystemCheck::App::UploadsPathTmpPermissionCheck,
+        SystemCheck::App::InitScriptExistsCheck,
+        SystemCheck::App::InitScriptUpToDateCheck,
+        SystemCheck::App::ProjectsHaveNamespaceCheck,
+        SystemCheck::App::RedisVersionCheck,
+        SystemCheck::App::RubyVersionCheck,
+        SystemCheck::App::GitVersionCheck,
+        SystemCheck::App::ActiveUsersCheck
+      ]
+
+      SystemCheck.run('GitLab', checks)
     end
   end
 
   namespace :gitlab_shell do
+    include SystemCheck::Helpers
+
     desc "GitLab | Check the configuration of GitLab Shell"
     task check: :environment  do
       warn_user_is_not_gitlab
@@ -513,33 +224,6 @@ namespace :gitlab do
       end
     end
 
-    def check_projects_have_namespace
-      print "projects have namespace: ... "
-
-      unless Project.count > 0
-        puts "can't check, you have no projects".color(:magenta)
-        return
-      end
-      puts ""
-
-      Project.find_each(batch_size: 100) do |project|
-        print sanitized_message(project)
-
-        if project.namespace
-          puts "yes".color(:green)
-        else
-          puts "no".color(:red)
-          try_fixing_it(
-            "Migrate global projects"
-          )
-          for_more_information(
-            "doc/update/5.4-to-6.0.md in section \"#global-projects\""
-          )
-          fix_and_rerun
-        end
-      end
-    end
-
     # Helper methods
     ########################
 
@@ -565,6 +249,8 @@ namespace :gitlab do
   end
 
   namespace :sidekiq do
+    include SystemCheck::Helpers
+
     desc "GitLab | Check the configuration of Sidekiq"
     task check: :environment  do
       warn_user_is_not_gitlab
@@ -623,6 +309,8 @@ namespace :gitlab do
   end
 
   namespace :incoming_email do
+    include SystemCheck::Helpers
+
     desc "GitLab | Check the configuration of Reply by email"
     task check: :environment  do
       warn_user_is_not_gitlab
@@ -757,6 +445,8 @@ namespace :gitlab do
   end
 
   namespace :ldap do
+    include SystemCheck::Helpers
+
     task :check, [:limit] => :environment do |_, args|
       # Only show up to 100 results because LDAP directories can be very big.
       # This setting only affects the `rake gitlab:check` script.
@@ -812,6 +502,8 @@ namespace :gitlab do
   end
 
   namespace :repo do
+    include SystemCheck::Helpers
+
     desc "GitLab | Check the integrity of the repositories managed by GitLab"
     task check: :environment do
       Gitlab.config.repositories.storages.each do |name, repository_storage|
@@ -826,6 +518,8 @@ namespace :gitlab do
   end
 
   namespace :user do
+    include SystemCheck::Helpers
+
     desc "GitLab | Check the integrity of a specific user's repositories"
     task :check_repos, [:username] => :environment do |t, args|
       username = args[:username] || prompt("Check repository integrity for fsername? ".color(:blue))
@@ -848,55 +542,6 @@ namespace :gitlab do
   # Helper methods
   ##########################
 
-  def fix_and_rerun
-    puts "  Please fix the error above and rerun the checks.".color(:red)
-  end
-
-  def for_more_information(*sources)
-    sources = sources.shift if sources.first.is_a?(Array)
-
-    puts "  For more information see:".color(:blue)
-    sources.each do |source|
-      puts "  #{source}"
-    end
-  end
-
-  def finished_checking(component)
-    puts ""
-    puts "Checking #{component.color(:yellow)} ... #{"Finished".color(:green)}"
-    puts ""
-  end
-
-  def see_database_guide
-    "doc/install/databases.md"
-  end
-
-  def see_installation_guide_section(section)
-    "doc/install/installation.md in section \"#{section}\""
-  end
-
-  def sudo_gitlab(command)
-    "sudo -u #{gitlab_user} -H #{command}"
-  end
-
-  def gitlab_user
-    Gitlab.config.gitlab.user
-  end
-
-  def start_checking(component)
-    puts "Checking #{component.color(:yellow)} ..."
-    puts ""
-  end
-
-  def try_fixing_it(*steps)
-    steps = steps.shift if steps.first.is_a?(Array)
-
-    puts "  Try fixing it:".color(:blue)
-    steps.each do |step|
-      puts "  #{step}"
-    end
-  end
-
   def check_gitlab_shell
     required_version = Gitlab::VersionInfo.new(gitlab_shell_major_version, gitlab_shell_minor_version, gitlab_shell_patch_version)
     current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
@@ -909,65 +554,6 @@ namespace :gitlab do
     end
   end
 
-  def check_ruby_version
-    required_version = Gitlab::VersionInfo.new(2, 1, 0)
-    current_version = Gitlab::VersionInfo.parse(run_command(%w(ruby --version)))
-
-    print "Ruby version >= #{required_version} ? ... "
-
-    if current_version.valid? && required_version <= current_version
-      puts "yes (#{current_version})".color(:green)
-    else
-      puts "no".color(:red)
-      try_fixing_it(
-        "Update your ruby to a version >= #{required_version} from #{current_version}"
-      )
-      fix_and_rerun
-    end
-  end
-
-  def check_git_version
-    required_version = Gitlab::VersionInfo.new(2, 7, 3)
-    current_version = Gitlab::VersionInfo.parse(run_command(%W(#{Gitlab.config.git.bin_path} --version)))
-
-    puts "Your git bin path is \"#{Gitlab.config.git.bin_path}\""
-    print "Git version >= #{required_version} ? ... "
-
-    if current_version.valid? && required_version <= current_version
-      puts "yes (#{current_version})".color(:green)
-    else
-      puts "no".color(:red)
-      try_fixing_it(
-        "Update your git to a version >= #{required_version} from #{current_version}"
-      )
-      fix_and_rerun
-    end
-  end
-
-  def check_active_users
-    puts "Active users: #{User.active.count}"
-  end
-
-  def omnibus_gitlab?
-    Dir.pwd == '/opt/gitlab/embedded/service/gitlab-rails'
-  end
-
-  def sanitized_message(project)
-    if should_sanitize?
-      "#{project.namespace_id.to_s.color(:yellow)}/#{project.id.to_s.color(:yellow)} ... "
-    else
-      "#{project.name_with_namespace.color(:yellow)} ... "
-    end
-  end
-
-  def should_sanitize?
-    if ENV['SANITIZE'] == "true"
-      true
-    else
-      false
-    end
-  end
-
   def check_repo_integrity(repo_dir)
     puts "\nChecking repo at #{repo_dir.color(:yellow)}"
 
diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/tasks/gitlab/task_helpers.rb
index e3c9d3b491c8e0460491bb7bad89eaf32d350d1a..964aa0fe1bc9e2346461a51eca0a8ba91c5c2aee 100644
--- a/lib/tasks/gitlab/task_helpers.rb
+++ b/lib/tasks/gitlab/task_helpers.rb
@@ -98,34 +98,30 @@ module Gitlab
       end
     end
 
+    def gitlab_user
+      Gitlab.config.gitlab.user
+    end
+
+    def is_gitlab_user?
+      return @is_gitlab_user unless @is_gitlab_user.nil?
+
+      current_user = run_command(%w(whoami)).chomp
+      @is_gitlab_user = current_user == gitlab_user
+    end
+
     def warn_user_is_not_gitlab
-      unless @warned_user_not_gitlab
-        gitlab_user = Gitlab.config.gitlab.user
+      return if @warned_user_not_gitlab
+
+      unless is_gitlab_user?
         current_user = run_command(%w(whoami)).chomp
-        unless current_user == gitlab_user
-          puts " Warning ".color(:black).background(:yellow)
-          puts "  You are running as user #{current_user.color(:magenta)}, we hope you know what you are doing."
-          puts "  Things may work\/fail for the wrong reasons."
-          puts "  For correct results you should run this as user #{gitlab_user.color(:magenta)}."
-          puts ""
-        end
-        @warned_user_not_gitlab = true
-      end
-    end
 
-    # Tries to configure git itself
-    #
-    # Returns true if all subcommands were successfull (according to their exit code)
-    # Returns false if any or all subcommands failed.
-    def auto_fix_git_config(options)
-      if !@warned_user_not_gitlab
-        command_success = options.map do |name, value|
-          system(*%W(#{Gitlab.config.git.bin_path} config --global #{name} #{value}))
-        end
+        puts " Warning ".color(:black).background(:yellow)
+        puts "  You are running as user #{current_user.color(:magenta)}, we hope you know what you are doing."
+        puts "  Things may work\/fail for the wrong reasons."
+        puts "  For correct results you should run this as user #{gitlab_user.color(:magenta)}."
+        puts ""
 
-        command_success.all?
-      else
-        false
+        @warned_user_not_gitlab = true
       end
     end
 
diff --git a/spec/db/production/settings.rb b/spec/db/production/settings.rb
index 007b35bbb77c6cdb3be5e5fb020bf56c7cf55f55..3cbb173c4cce5f1e37653bfea907a779858f6399 100644
--- a/spec/db/production/settings.rb
+++ b/spec/db/production/settings.rb
@@ -1,5 +1,4 @@
 require 'spec_helper'
-require 'rainbow/ext/string'
 
 describe 'seed production settings', lib: true do
   include StubENV
diff --git a/spec/lib/system_check/simple_executor_spec.rb b/spec/lib/system_check/simple_executor_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a5c6170cd7de77eb1f3e7401fe44434c21617aa9
--- /dev/null
+++ b/spec/lib/system_check/simple_executor_spec.rb
@@ -0,0 +1,223 @@
+require 'spec_helper'
+require 'rake_helper'
+
+describe SystemCheck::SimpleExecutor, lib: true do
+  class SimpleCheck < SystemCheck::BaseCheck
+    set_name 'my simple check'
+
+    def check?
+      true
+    end
+  end
+
+  class OtherCheck < SystemCheck::BaseCheck
+    set_name 'other check'
+
+    def check?
+      false
+    end
+
+    def show_error
+      $stdout.puts 'this is an error text'
+    end
+  end
+
+  class SkipCheck < SystemCheck::BaseCheck
+    set_name 'skip check'
+    set_skip_reason 'this is a skip reason'
+
+    def skip?
+      true
+    end
+
+    def check?
+      raise 'should not execute this'
+    end
+  end
+
+  class MultiCheck < SystemCheck::BaseCheck
+    set_name 'multi check'
+
+    def multi_check
+      $stdout.puts 'this is a multi output check'
+    end
+
+    def check?
+      raise 'should not execute this'
+    end
+  end
+
+  class SkipMultiCheck < SystemCheck::BaseCheck
+    set_name 'skip multi check'
+
+    def skip?
+      true
+    end
+
+    def multi_check
+      raise 'should not execute this'
+    end
+  end
+
+  class RepairCheck < SystemCheck::BaseCheck
+    set_name 'repair check'
+
+    def check?
+      false
+    end
+
+    def repair!
+      true
+    end
+
+    def show_error
+      $stdout.puts 'this is an error message'
+    end
+  end
+
+  describe '#component' do
+    it 'returns stored component name' do
+      expect(subject.component).to eq('Test')
+    end
+  end
+
+  describe '#checks' do
+    before do
+      subject << SimpleCheck
+    end
+
+    it 'returns a set of classes' do
+      expect(subject.checks).to include(SimpleCheck)
+    end
+  end
+
+  describe '#<<' do
+    before do
+      subject << SimpleCheck
+    end
+
+    it 'appends a new check to the Set' do
+      subject << OtherCheck
+      stored_checks = subject.checks.to_a
+
+      expect(stored_checks.first).to eq(SimpleCheck)
+      expect(stored_checks.last).to eq(OtherCheck)
+    end
+
+    it 'inserts unique itens only' do
+      subject << SimpleCheck
+
+      expect(subject.checks.size).to eq(1)
+    end
+  end
+
+  subject { described_class.new('Test') }
+
+  describe '#execute' do
+    before do
+      silence_output
+
+      subject << SimpleCheck
+      subject << OtherCheck
+    end
+
+    it 'runs included checks' do
+      expect(subject).to receive(:run_check).with(SimpleCheck)
+      expect(subject).to receive(:run_check).with(OtherCheck)
+
+      subject.execute
+    end
+  end
+
+  describe '#run_check' do
+    it 'prints check name' do
+      expect(SimpleCheck).to receive(:display_name).and_call_original
+      expect { subject.run_check(SimpleCheck) }.to output(/my simple check/).to_stdout
+    end
+
+    context 'when check pass' do
+      it 'prints yes' do
+        expect_any_instance_of(SimpleCheck).to receive(:check?).and_call_original
+        expect { subject.run_check(SimpleCheck) }.to output(/ \.\.\. yes/).to_stdout
+      end
+    end
+
+    context 'when check fails' do
+      it 'prints no' do
+        expect_any_instance_of(OtherCheck).to receive(:check?).and_call_original
+        expect { subject.run_check(OtherCheck) }.to output(/ \.\.\. no/).to_stdout
+      end
+
+      it 'displays error message from #show_error' do
+        expect_any_instance_of(OtherCheck).to receive(:show_error).and_call_original
+        expect { subject.run_check(OtherCheck) }.to output(/this is an error text/).to_stdout
+      end
+
+      context 'when check implements #repair!' do
+        it 'executes #repair!' do
+          expect_any_instance_of(RepairCheck).to receive(:repair!)
+
+          subject.run_check(RepairCheck)
+        end
+
+        context 'when repair succeeds' do
+          it 'does not execute #show_error' do
+            expect_any_instance_of(RepairCheck).to receive(:repair!).and_call_original
+            expect_any_instance_of(RepairCheck).not_to receive(:show_error)
+
+            subject.run_check(RepairCheck)
+          end
+        end
+
+        context 'when repair fails' do
+          it 'does not execute #show_error' do
+            expect_any_instance_of(RepairCheck).to receive(:repair!) { false }
+            expect_any_instance_of(RepairCheck).to receive(:show_error)
+
+            subject.run_check(RepairCheck)
+          end
+        end
+      end
+    end
+
+    context 'when check implements skip?' do
+      it 'executes #skip? method' do
+        expect_any_instance_of(SkipCheck).to receive(:skip?).and_call_original
+
+        subject.run_check(SkipCheck)
+      end
+
+      it 'displays #skip_reason' do
+        expect { subject.run_check(SkipCheck) }.to output(/this is a skip reason/).to_stdout
+      end
+
+      it 'does not execute #check when #skip? is true' do
+        expect_any_instance_of(SkipCheck).not_to receive(:check?)
+
+        subject.run_check(SkipCheck)
+      end
+    end
+
+    context 'when implements a #multi_check' do
+      it 'executes #multi_check method' do
+        expect_any_instance_of(MultiCheck).to receive(:multi_check)
+
+        subject.run_check(MultiCheck)
+      end
+
+      it 'does not execute #check method' do
+        expect_any_instance_of(MultiCheck).not_to receive(:check)
+
+        subject.run_check(MultiCheck)
+      end
+
+      context 'when check implements #skip?' do
+        it 'executes #skip? method' do
+          expect_any_instance_of(SkipMultiCheck).to receive(:skip?).and_call_original
+
+          subject.run_check(SkipMultiCheck)
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/system_check_spec.rb b/spec/lib/system_check_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..23d9beddb088a29558d66511a0cbe342d0e9dc61
--- /dev/null
+++ b/spec/lib/system_check_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+require 'rake_helper'
+
+describe SystemCheck, lib: true do
+  class SimpleCheck < SystemCheck::BaseCheck
+    def check?
+      true
+    end
+  end
+
+  class OtherCheck < SystemCheck::BaseCheck
+    def check?
+      false
+    end
+  end
+
+  before do
+    silence_output
+  end
+
+  describe '.run' do
+    subject { SystemCheck }
+
+    it 'detects execution of SimpleCheck' do
+      is_expected.to execute_check(SimpleCheck)
+
+      subject.run('Test', [SimpleCheck])
+    end
+
+    it 'detects exclusion of OtherCheck in execution' do
+      is_expected.not_to execute_check(OtherCheck)
+
+      subject.run('Test', [SimpleCheck])
+    end
+  end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 4c2eba8fa46812f92662db977101078bc99667c9..994c7dcbb46229b45ddf64391b093ef882056890 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -26,6 +26,9 @@ if ENV['CI'] && !ENV['NO_KNAPSACK']
   Knapsack::Adapters::RSpecAdapter.bind
 end
 
+# require rainbow gem String monkeypatch, so we can test SystemChecks
+require 'rainbow/ext/string'
+
 # Requires supporting ruby files with custom matchers and macros, etc,
 # in spec/support/ and its subdirectories.
 Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
diff --git a/spec/support/matchers/execute_check.rb b/spec/support/matchers/execute_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7232fad52fbe21fe9b844bfdca26150342901974
--- /dev/null
+++ b/spec/support/matchers/execute_check.rb
@@ -0,0 +1,23 @@
+RSpec::Matchers.define :execute_check do |expected|
+  match do |actual|
+    expect(actual).to eq(SystemCheck)
+    expect(actual).to receive(:run) do |*args|
+      expect(args[1]).to include(expected)
+    end
+  end
+
+  match_when_negated do |actual|
+    expect(actual).to eq(SystemCheck)
+    expect(actual).to receive(:run) do |*args|
+      expect(args[1]).not_to include(expected)
+    end
+  end
+
+  failure_message do |actual|
+    'This matcher must be used with SystemCheck' unless actual == SystemCheck
+  end
+
+  failure_message_when_negated do |actual|
+    'This matcher must be used with SystemCheck' unless actual == SystemCheck
+  end
+end
diff --git a/spec/support/rake_helpers.rb b/spec/support/rake_helpers.rb
index 4a8158ed79bde74cb9f26d20e65b35f86aa46126..5cb415111d277cc6cf860a6a4ae9af2895c54959 100644
--- a/spec/support/rake_helpers.rb
+++ b/spec/support/rake_helpers.rb
@@ -7,4 +7,9 @@ module RakeHelpers
   def stub_warn_user_is_not_gitlab
     allow_any_instance_of(Object).to receive(:warn_user_is_not_gitlab)
   end
+
+  def silence_output
+    allow($stdout).to receive(:puts)
+    allow($stdout).to receive(:print)
+  end
 end