diff --git a/check_gitlab.rb b/check_gitlab.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f8d3dc59117bf74556b45e0f70e8603b125bc2da
--- /dev/null
+++ b/check_gitlab.rb
@@ -0,0 +1,404 @@
+#!/opt/gitlab/embedded/bin/ruby
+# frozen_string_literal: true
+
+#
+# Gitlab Plugin
+# ==
+# Author: Marco Peterseil
+# Created: 03-2017
+# License: GPLv3 - http://www.gnu.org/licenses
+# URL: https://gitlab.com/6uellerBpanda/check_gitlab
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+require 'optparse'
+require 'net/https'
+require 'json'
+require 'date'
+
+version = 'v0.6.0'
+
+# optparser
+banner = <<~HEREDOC
+  check_gitlab #{version} [https://gitlab.com/6uellerBpanda/check_gitlab]\n
+  This plugin checks various parameters of Gitlab\n
+  Mode:
+    health                    Check the Gitlab web endpoint for health
+    services                  Check if any service of 'gitlab-ctl status' is down
+    group-size                Check size of group in MB
+    ci-pipeline-duration      Check duration of a CI pipeline
+    ci-pipeline-status        Check status of a CI pipeline
+    ci-runner-status          Check status of CI runners
+    ci-runner-jobs-duration   Check duration (in seconds) of running jobs of CI runners
+    license-expires           Check remaining days when license expires - only warning status possible
+    license-overage           Check if more active then licensed users are present
+    sidekiq-jobs              Check size of sidekiq jobs
+
+  Usage: #{File.basename(__FILE__)} [options]
+HEREDOC
+
+options = {}
+OptionParser.new do |opts| # rubocop:disable  Metrics/BlockLength
+  opts.banner = banner.to_s
+  opts.separator ''
+  opts.separator 'Options:'
+  opts.on('-s', '--address ADDRESS', '-H', 'Gitlab address') do |s|
+    options[:address] = s
+  end
+  opts.on('-t', '--token TOKEN', 'Access token') do |t|
+    options[:token] = t
+  end
+  opts.on('-i', '--id ID', 'Project/Group/CI-Runner ID') do |i|
+    options[:id] = i
+  end
+  opts.on('-k', '--insecure', 'No ssl verification') do |k|
+    options[:insecure] = k
+  end
+  opts.on('-m', '--mode MODE', 'Mode to check') do |m|
+    options[:mode] = m
+  end
+  opts.on('-n', '--name NAME', 'Name of group (regex), or sidekiq jobs') do |n|
+    options[:name] = n
+  end
+  opts.on('-e', '--exclude EXCLUDE', 'Exclude (regex)') do |e|
+    options[:exclude] = e
+  end
+  opts.on('--status STATUS', 'Status to use') do |status|
+    options[:status] = status
+  end
+  opts.on('-w', '--warning WARNING', 'Warning threshold') do |w|
+    options[:warning] = w
+  end
+  opts.on('-c', '--critical CRITICAL', 'Critical threshold') do |c|
+    options[:critical] = c
+  end
+  opts.on('-d', '--debug', 'Print extra debugging/status output (available for health check)') do |d|
+    options[:debug] = d
+  end
+  opts.on('-v', '--version', 'Print version information') do
+    puts "check_gitlab #{version}"
+  end
+  opts.on('-h', '--help', 'Show this help message') do
+    puts opts
+  end
+  ARGV.push('-h') if ARGV.empty?
+end.parse!
+
+# check gitlab
+class CheckGitlab
+  def initialize(options) # rubocop:disable Metrics/MethodLength
+    @options = options
+    init_arr
+    validate_check_modes
+    health_check
+    ci_pipeline_status
+    ci_pipeline_duration
+    ci_runner_jobs_duration
+    ci_runner_status
+    services_check
+    group_size
+    license_expire
+    license_overage
+    sidekiq_jobs
+  end
+
+  #--------#
+  # HELPER #
+  #--------#
+
+  def init_arr
+    @perfdata = []
+    @message = []
+    @critical = []
+    @warning = []
+    @okays = []
+  end
+
+  # define some helper methods for naemon
+  def ok_msg(message)
+    puts "OK - #{message}" + @debug.to_s
+    exit 0
+  end
+
+  def crit_msg(message)
+    puts "Critical - #{message}" + @debug.to_s
+    exit 2
+  end
+
+  def warn_msg(message)
+    puts "Warning - #{message}" + @debug.to_s
+    exit 1
+  end
+
+  def unk_msg(message)
+    puts "Unknown - #{message}"
+    exit 3
+  end
+
+  def validate_check_modes
+    check_modes = %w[
+      health ci-pipeline-duration ci-pipeline-status
+      ci-runner-jobs-duration ci-runner-status services group-size
+      sidekiq-jobs license-expire license-overage
+    ]
+    warn_msg('Mode not found. Check configuration.') unless check_modes.include?(@options[:mode])
+  end
+
+  # convert the bytes
+  def convert_to_mb(data:)
+    @used_size = data.to_i / 1024 / 1024
+  end
+
+  # debug output
+  def debug(data:)
+    @debug = data.map { |k, v| "\n#{k} is #{v[0]['status']}" }.join
+  end
+
+  def build_perfdata(perfdata:)
+    @perfdata << "#{perfdata};#{@options[:warning]};#{@options[:critical]}"
+  end
+
+  # build service output
+  def build_output(msg:)
+    @message = msg
+  end
+
+  ### helpers for threshold checking
+  def check_thresholds_int(data:, type: 'non-array')
+    if data > @options[:critical].to_i
+      @critical << @message
+    elsif data > @options[:warning].to_i
+      @warning << @message
+    else
+      @okays << @message
+    end
+    # make the final step
+    build_final_output unless type != 'non-array'
+  end
+
+  def check_thresholds_string(data:)
+    case data
+    when /#{@options[:critical]}/
+      @critical << @message
+    when /#{@options[:warning]}/
+      @warning << @message
+    else
+      @okays << @message
+    end
+    build_final_output(perf_enabled: false)
+  end
+
+  # mix everything together for exit
+  def build_final_output(pretext: '', perf_enabled: true)
+    perf_output = " | #{@perfdata.join(' ')}" unless perf_enabled == false
+    if @critical.any?
+      crit_msg(pretext + @critical.join(', ') + perf_output.to_s)
+    elsif @warning.any?
+      warn_msg(pretext + @warning.join(', ') + perf_output.to_s)
+    else
+      ok_msg(pretext + @okays.join(', ') + perf_output.to_s)
+    end
+  end
+
+  #----------#
+  # API AUTH #
+  #----------#
+
+  # create url
+  def url(path:)
+    uri = URI("#{@options[:address]}/#{path}")
+    http = Net::HTTP.new(uri.host, uri.port)
+    http.use_ssl = true
+    http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @options[:insecure]
+    request = Net::HTTP::Get.new(uri.request_uri)
+    request.add_field 'PRIVATE-TOKEN', @options[:token] if @options[:mode] != 'health'
+    @response = http.request(request)
+  rescue StandardError => e
+    unk_msg(e)
+  end
+
+  # init http req
+  def http_connect(path:)
+    url(path: path)
+    check_http_response
+  end
+
+  # check http response
+  def check_http_response
+    unk_msg(@response.message).to_s if @response.code != '200'
+  end
+
+  #--------#
+  # CHECKS #
+  #--------#
+
+  ###--- HEALTH CHECK ---###
+  def health_check
+    return unless @options[:mode] == 'health'
+    http_connect(path: '-/readiness?all=1')
+    data_json = JSON.parse(@response.body)
+    data_json.delete('status')
+    unhealthy_probes = data_json.reject { |_k, v| v[0]['status'] == 'ok' }
+    debug(data: data_json) if @options[:debug]
+    if unhealthy_probes.empty?
+      ok_msg('Gitlab probes are in healthy state')
+    else
+      warn_msg(unhealthy_probes.map { |k, _v| k }.join(', ') + ' probe has problems') # rubocop:disable Style/StringConcatenation
+    end
+  end
+
+  ###--- CI-PIPELINE STATUS CHECK ---###
+  def ci_pipeline_status
+    return unless @options[:mode] == 'ci-pipeline-status'
+    http_connect(path: "api/v4/projects/#{@options[:id]}/pipelines")
+    ci_pipeline_data = JSON.parse(@response.body).first
+    build_output(msg: "Status of pipeline ##{ci_pipeline_data['id']}: #{ci_pipeline_data['status'].capitalize}")
+    check_thresholds_string(data: ci_pipeline_data['status'])
+  end
+
+  ###--- CI-PIPELINE DURATION CHECK ---###
+  def ci_pipeline_duration
+    return unless @options[:mode] == 'ci-pipeline-duration'
+    http_connect(path: "api/v4/projects/#{@options[:id]}/pipelines?scope=finished")
+    # get latest pipeline
+    http_connect(path: "api/v4/projects/#{@options[:id]}/pipelines/#{JSON.parse(@response.body).first['id']}")
+    ci_pipeline_data = JSON.parse(@response.body)
+    build_output(msg: "Pipeline ##{ci_pipeline_data['id']} took #{ci_pipeline_data['duration']}s")
+    build_perfdata(perfdata: "duration=#{ci_pipeline_data['duration']}s")
+    check_thresholds_int(data: ci_pipeline_data['duration'])
+  end
+
+  ###--- CI-RUNNER ---###
+  def ci_runner_get_all(status:)
+    http_connect(path: "api/v4/runners/all?status=#{status.downcase}&per_page=100")
+    @all_runners = JSON.parse(@response.body)
+    @all_runners.delete_if { |item| /#{@options[:exclude]}/.match(item['description']) } unless @options[:exclude].to_s.empty?
+    # bail out if nothing comes back
+    if @all_runners.empty? && @options[:status] == 'online'
+      crit_msg("No #{@options[:status]} runners")
+    elsif @all_runners.empty?
+      ok_msg("No #{@options[:status]} runners")
+    end
+  end
+
+  ### jobs duration
+  def ci_runner_jobs_duration
+    return unless @options[:mode] == 'ci-runner-jobs-duration'
+    http_connect(path: "api/v4/runners/#{@options[:id]}/jobs?status=running")
+    jobs = JSON.parse(@response.body).first
+    if jobs.nil?
+      ok_msg('No running jobs')
+    else
+      build_output(msg: "#{jobs['name']} is running for #{jobs['duration'].round}s")
+      build_perfdata(perfdata: "duration=#{jobs['duration'].round}s")
+      check_thresholds_int(data: jobs['duration'])
+    end
+  end
+
+  ### runner status
+  def ci_runner_status
+    return unless @options[:mode] == 'ci-runner-status'
+    ci_runner_get_all(status: @options[:status])
+    @all_runners.each do |item|
+      http_connect(path: "api/v4/runners/#{item['id']}")
+      @runners = JSON.parse(@response.body)
+      build_output(msg: "#{@runners['description']}(#{@runners['id']})")
+      check_thresholds_int(type: 'array', data: @all_runners.count)
+    end
+    build_perfdata(perfdata: "#{@options[:status]}=#{@all_runners.count}")
+    build_final_output(pretext: "#{@options[:status].capitalize} runners: ")
+  end
+
+  ###--- SERVICES CHECK ---###
+  # check if sudoers entry with 'gitlab-ctl' command is present for current user
+  def gitlab_ctl_sudoers_check
+    `sudo -ln gitlab-ctl status 2>/dev/null`
+    unk_msg('No sudoers entry found for gitlab-ctl command') unless $?.success? # rubocop:disable Style/SpecialGlobalVars
+  rescue StandardError => e
+    unk_msg(e)
+  end
+
+  # get all services with down status
+  def gitlab_ctl_status
+    `sudo gitlab-ctl status`.scan(/(?:down: )(\w+.\w+)/)
+  rescue StandardError => e
+    unk_msg(e)
+  end
+
+  def services_check
+    return unless @options[:mode] == 'services'
+    gitlab_ctl_sudoers_check
+    if gitlab_ctl_status.any?
+      down_srvc = gitlab_ctl_status.join(', ')
+      crit_msg("#{down_srvc} is down")
+    else
+      ok_msg('All services are running')
+    end
+  end
+
+  ###--- GROUP SIZE ---###
+  def groups_get_all
+    http_connect(path: 'api/v4/groups?statistics=true&per_page=100')
+    @all_groups = JSON.parse(@response.body)
+    @all_groups.delete_if { |item| /#{@options[:exclude]}/.match(item['name']) } unless @options[:exclude].to_s.empty?
+    @all_groups.keep_if { |item| /#{@options[:name]}/.match(item['name']) } unless @options[:name].to_s.empty?
+  end
+
+  def group_size
+    return unless @options[:mode] == 'group-size'
+    groups_get_all
+    @all_groups.each do |item|
+      convert_to_mb(data: item['statistics']['storage_size'])
+      build_output(msg: "#{item['name']}: used space #{@used_size}MB")
+      check_thresholds_int(type: 'array', data: @used_size)
+      build_perfdata(perfdata: "#{item['name']}=#{@used_size}MB")
+    end
+    build_final_output
+  end
+
+  ###--- LICENSE ---###
+  def license_expire
+    return unless @options[:mode] == 'license-expire'
+    http_connect(path: 'api/v4/license')
+    due_date = JSON.parse(@response.body)['expires_at']
+    expire = Date.parse(due_date) - Date.today
+    if expire.to_i > @options[:warning].to_i
+      ok_msg("License  will expire at #{due_date} - #{expire.to_i} days left")
+    else
+      warn_msg("License  will expire at #{due_date} - #{expire.to_i} days left")
+    end
+  end
+
+  def license_overage
+    return unless @options[:mode] == 'license-overage'
+    http_connect(path: 'api/v4/license')
+    data = JSON.parse(@response.body)
+    build_output(msg: "Active users: #{data['active_users']}, Overage: #{data['overage']}")
+    build_perfdata(perfdata: "active_users=#{data['active_users']} overage=#{data['overage']}")
+    check_thresholds_int(data: data['overage'])
+  end
+
+  ###--- SIDEKIQ ---###
+  def sidekiq_jobs
+    return unless @options[:mode] == 'sidekiq-jobs'
+    http_connect(path: 'api/v4/sidekiq/job_stats')
+    data = JSON.parse(@response.body)['jobs']
+    build_output(msg: "Sidekiq jobs - #{@options[:name].capitalize}: #{data[@options[:name]]}")
+    build_perfdata(perfdata: "#{@options[:name]}=#{data[@options[:name]]}")
+    check_thresholds_int(data: data[@options[:name]])
+  end
+end
+
+CheckGitlab.new(options)