GitLab Rails Console Cheat Sheet (FREE SELF)

This is the GitLab Support Team's collection of information regarding the GitLab Rails console, for use while troubleshooting. It is listed here for transparency, and it may be useful for users with experience with these tools. If you are currently having an issue with GitLab, it is highly recommended that you first check our guide on navigating our Rails console, and your support options, before attempting to use this information.

WARNING: Please note that some of these scripts could be damaging if not run correctly, or under the right conditions. We highly recommend running them under the guidance of a Support Engineer, or running them in a test environment with a backup of the instance ready to be restored, just in case.

WARNING: Please also note that as GitLab changes, changes to the code are inevitable, and so some scripts may not work as they once used to. These are not kept up-to-date as these scripts/commands were added as they were found/needed. As mentioned above, we recommend running these scripts under the supervision of a Support Engineer, who can also verify that they will continue to work as they should and, if needed, update the script for the latest version of GitLab.

Find specific methods for an object { |m| m.to_s.include? "sing" }

Find method source

Works for non-instrumented methods:


# Example for when we would call project.private?


View available attributes, formatted using pretty print (pp).

For example, determine what attributes contain users' names and email addresses:

u = User.find_by_username('someuser')
pp u.attributes

Partial output:

 "name"=>"S User",

Then make use of the attributes, testing SMTP, for example:

e =
n =
Notify.test_email(e, "Test email for #{n}", 'Test email').deliver_now
Notify.test_email(, "Test email for #{}", 'Test email').deliver_now

Query the database using an ActiveRecord Model

m = Model.where('attribute like ?', 'ex%')

# for example to query the projects
projects = Project.where('path like ?', 'Oumua%')

View all keys in cache


Profile a page

# Before 11.6.0
logger =
admin_token = User.find_by_username('ADMIN_USERNAME').personal_access_tokens.first.token

# From 11.6.0
admin = User.find_by_username('ADMIN_USERNAME')
url = "/url/goes/here"
Gitlab::Profiler.with_user(admin) { app.get(url) }

Using the GitLab profiler inside console (used as of 10.5)

logger =
admin = User.find_by_username('ADMIN_USERNAME')
Gitlab::Profiler.profile('URL', logger: logger, user: admin)

Time an operation

# A single operation
Benchmark.measure { <operation> }

# A breakdown of multiple operations do |x| { <operation_1> } { <operation_2> }

Feature flags

Show all feature flags that are enabled

# Regular output

# Nice output {|f| [, f.state]}

Command Line

Check the GitLab version fast

grep -m 1 gitlab /opt/gitlab/version-manifest.txt

Debugging SSH

GIT_SSH_COMMAND="ssh -vvv" git clone <repository>

Debugging over HTTPS

GIT_CURL_VERBOSE=1 GIT_TRACE=1 git clone <repository>


Clear a project's cache


Expire the .exists? cache


Make all projects private

Project.update_all(visibility_level: 0)

Find projects that are pending deletion

# This section will list all the projects which are pending deletion
projects = Project.where(pending_delete: true)
projects.each do |p|
  puts "Project ID: #{}"
  puts "Project name: #{}"
  puts "Repository path: #{p.repository.full_path}"

# Assign a user (the root user will do)
user = User.find_by_username('root')

# For each project listed repeat these two commands

# Find the project, update the xxx-changeme values from above
project = Project.find_by_full_path('group-changeme/project-changeme')

# Immediately delete the project, user, {}).execute

Destroy a project

project = Project.find_by_full_path('<project_path>')
user = User.find_by_username('<username>')
ProjectDestroyWorker.perform_async(,, {})
# or,, {})
# or, user).execute

Remove fork relationship manually

p = Project.find_by_full_path('<project_path>')
u = User.find_by_username('<username>'), u).execute

Make a project read-only (can only be done in the console)

# Make a project read-only
project.repository_read_only = true;

# OR
project.update!(repository_read_only: true)

Transfer project from one namespace to another

 p= Project.find_by_full_path('<project_path>')

 # To set the owner of the project
 current_user= p.creator

# Namespace where you want this to be moved.
namespace = Namespace.find_by_full_path("<new_namespace>"), current_user).execute(namespace)

For Removing webhooks that is getting timeout due to large webhook logs

# ID will be the webhook_id

#In case the service gets timeout consider removing webhook_logs

Bulk update service integration password for all projects

For example, change the Jira user's password for all projects that have the Jira integration active:

p = Project.find_by_sql("SELECT FROM projects p LEFT JOIN services s ON = s.project_id WHERE s.type = 'JiraService' AND = true")

p.each do |project|
  project.jira_service.update_attribute(:password, '<your-new-password>')

Bulk update to disable the Slack Notification service

To disable notifications for all projects that have Slack service enabled, do:

# Grab all projects that have the Slack notifications enabled
p = Project.find_by_sql("SELECT FROM projects p LEFT JOIN services s ON = s.project_id WHERE s.type = 'SlackService' AND = true")

# Disable the service on each of the projects that were found.
p.each do |project|
  project.slack_service.update_attribute(:active, false)

Incorrect repository statistics shown in the GUI

After reducing a repository size with third-party tools the displayed size may still show old sizes or commit numbers. To force an update, do:

p = Project.find_by_full_path('<namespace>/<project>')
pp p.statistics
pp p.statistics  # compare with earlier values

Identify deploy keys associated with blocked and non-member users

When the user who created a deploy key is blocked or removed from the project, the key can no longer be used to push to protected branches in a private project (see issue #329742). The following script identifies unusable deploy keys:

ghost_user_id =

DeployKeysProject.with_write_access.find_each do |deploy_key_mapping|
  project = deploy_key_mapping.project
  deploy_key = deploy_key_mapping.deploy_key
  user = deploy_key.user

  access_checker =, container: project)

  # can_push_for_ref? tests if deploy_key can push to default branch, which is likely to be protected
  can_push = access_checker.can_do_action?(:push_code)
  can_push_to_default = access_checker.can_push_for_ref?(project.repository.root_ref)
  next if access_checker.allowed? && can_push && can_push_to_default

  if user.nil? || == ghost_user_id
    username = 'none'
    state = '-'
    username = user.username
    user_state = user.state

  puts "Deploy key: #{}, Project: #{project.full_path}, Can push?: " + (can_push ? 'YES' : 'NO') +
       ", Can push to default branch #{project.repository.root_ref}?: " + (can_push_to_default ? 'YES' : 'NO') +
       ", User: #{username}, User state: #{user_state}"



WARNING: This is a destructive operation, the Wiki will be empty.

A Projects Wiki can be recreated by this command:

p = Project.find_by_full_path('<username-or-group>/<project-name>')  ### enter your projects path

GitlabShellWorker.perform_in(0, :remove_repository, p.repository_storage,  ### deletes the wiki project from the filesystem

p.create_wiki  ### creates the wiki project on the filesystem

Issue boards

In case of issue boards not loading properly and it's getting time out. We need to call the Issue Rebalancing service to fix this

p=Project.find_by_full_path('PROJECT PATH')

Imports / Exports

# Find the project and get the error
p = Project.find_by_full_path('<username-or-group>/<project-name>')


# To finish the import on GitLab running version before 11.6

# To finish the import on GitLab running version 11.6 or after
p.import_state.mark_as_failed("Failed manually through console.")

Rename imported repository

In a specific situation, an imported repository needed to be renamed. The Support Team was informed of a backup restore that failed on a single repository, which created the project with an empty repository. The project was successfully restored to a development instance, then exported, and imported into a new project under a different name.

The Support Team was able to transfer the incorrectly named imported project into the correctly named empty project using the steps below.

Move the new repository to the empty repository:

mv /var/opt/gitlab/git-data/repositories/<group>/<new-project> /var/opt/gitlab/git-data/repositories/<group>/<empty-project>

Make sure the permissions are correct:

chown -R git:git <path-to-directory>.git

Clear the cache:

sudo gitlab-rake cache:clear

Export a repository

It's typically recommended to export a project through the web interface or through the API. In situations where this is not working as expected, it may be preferable to export a project directly via the Rails console:

user = User.find_by_username('USERNAME')
project = Project.find_by_full_path('PROJECT_PATH'), user).execute

If the project you wish to export is available at, the value to use for PROJECT_PATH would be baltig/pipeline-templates.

If this all runs successfully, you will see output like the following before being returned to the Rails console prompt:

=> nil

The exported project will be located within a .tar.gz file in /var/opt/gitlab/gitlab-rails/uploads/-/system/import_export_upload/export_file/.


Search sequence of pushes to a repository

If it seems that a commit has gone "missing", search the sequence of pushes to a repository. This StackOverflow article describes how you can end up in this state without a force push.

If you look at the output from the sample code below for the target branch, you will see a discontinuity in the from/to commits as you step through the output. Each new push should be "from" the "to" SHA of the previous push. When this discontinuity happens, you will see two pushes with the same "from" SHA:

p = Project.find_with_namespace('u/p') do |e|
  printf "%-20.20s %8s...%8s (%s)\n",[:ref],[:before],[:after],

GitLab 9.5 and above:

p = Project.find_by_full_path('u/p') do |e|
  printf "%-20.20s %8s...%8s (%s)\n", e.push_event_payload[:ref], e.push_event_payload[:commit_from], e.push_event_payload[:commit_to],


Find mirrors with "bad decrypt" errors

This content has been converted to a Rake task, see the Doctor Rake tasks docs.

Transfer mirror users and tokens to a single service account

Use case: If you have multiple users using their own GitHub credentials to set up repository mirroring, mirroring breaks when people leave the company. Use this script to migrate disparate mirroring users and tokens into a single service account:

svc_user = User.find_by(username: 'ourServiceUser')
token = 'githubAccessToken'

Project.where(mirror: true).each do |project|
  import_url = project.import_url

  # The url we want is https://token@project/path.git
  repo_url = if import_url.include?('@')
               # Case 1: The url is something like https://23423432@project/path.git
             elsif import_url.include?('//')
               # Case 2: The url is something like https://project/path.git

  next unless repo_url

  final_url = "https://#{token}@#{repo_url}"

  project.mirror_user = svc_user
  project.import_url = final_url
  project.username_only_import_url = final_url


Skip reconfirmation

user = User.find_by_username '<username>'

Active users & Historical users

# Active users on the instance, now

# Users taking a seat on the instance

# The historical max on the instance as of the past year

Using cURL and jq (up to a max 100, see the pagination docs):

curl --silent --header "Private-Token: ********************" "" | jq --compact-output '.[] | [.id,.name,.username]'

Block or Delete Users that have no projects or groups

users = User.where('id NOT IN (select distinct(user_id) from project_authorizations)')

# How many users will be removed?

# If that count looks sane:

# You can either block the users:
users.each { |user| user.block! }

# Or you can delete them:
  # need 'current user' (your user) for auditing purposes
current_user = User.find_by(username: '<your username>')

users.each do |user|

Deactivate Users that have no recent activity

days_inactive = 90
inactive_users ="last_activity_on <= ?", days_inactive.days.ago)

inactive_users.each do |user|
    puts "user '#{user.username}': #{user.last_activity_on}"

Block Users that have no recent activity

days_inactive = 90
inactive_users ="last_activity_on <= ?", days_inactive.days.ago)

inactive_users.each do |user|
    puts "user '#{user.username}': #{user.last_activity_on}"

Find a user's max permissions for project/group

user = User.find_by_username 'username'
project = Project.find_by_full_path 'group/project'
user = User.find_by_username 'username'
group = Group.find_by_full_path 'group'


Transfer group to another location

user = User.find_by_username('<username>')
group = Group.find_by_name("<group_name>")
parent_group = Group.find_by(id: "<group_id>")
service =, user)

Count unique users in a group and subgroups

group = Group.find_by_path_or_name("groupname")
members = []
for member in group.members_with_descendants

group = Group.find_by_path_or_name("groupname")

# Count users from subgroup and up (inherited)

# Count users from the parent group and down (specific grants)

Delete a group

GroupDestroyWorker.perform_async(group_id, user_id)

Modify group project creation

# Project creation levels: 0 - No one, 1 - Maintainers, 2 - Developers + Maintainers
group = Group.find_by_path_or_name('group-name')

Modify group - disable 2FA requirement

WARNING: When disabling the 2FA Requirement on a subgroup, the whole parent group (including all subgroups) is affected by this change.

group = Group.find_by_path_or_name('group-name')


Fixing bad SCIM identities

def delete_bad_scim(email, group_path)
    output = ""
    u = User.find_by_email(email)
    uid =
    g = Group.find_by_full_path(group_path)
    saml_prov_id = SamlProvider.find_by(group_id:
    saml = Identity.where(user_id: uid, saml_provider_id: saml_prov_id)
    scim = ScimIdentity.where(user_id: uid , group_id:
    if saml[0]
      saml_eid = saml[0].extern_uid
      output +=  "%s," % [email]
      output +=  "SAML: %s," % [saml_eid]
      if scim[0]
        scim_eid = scim[0].extern_uid
        output += "SCIM: %s" % [scim_eid]
        if saml_eid == scim_eid
          output += " Identities matched, not deleted \n"
          output += " Deleted \n"
        output = "ERROR No SCIM identify found for: [%s]\n" % [email]
        puts output
        return 1
      output = "ERROR No SAML identify found for: [%s]\n" % [email]
      puts output
      return 1
      puts output
    return 0

# In case of multiple emails
emails = [email1, email2]

emails.each do |e|


Remove redirecting routes


path = 'foo'
conflicting_permanent_redirects = RedirectRoute.matching_path_and_descendants(path)

# Check that conflicting_permanent_redirects is as expected

Merge Requests

Close a merge request properly (if merged but still marked as open)

p = Project.find_by_full_path('<full/path/to/project>')
m = p.merge_requests.find_by(iid: <iid>)
u = User.find_by_username('<username>'), u).execute(m)

Delete a merge request

u = User.find_by_username('<username>')
p = Project.find_by_full_path('<group>/<project>')
m = p.merge_requests.find_by(iid: <IID>), u).execute(m)

Rebase manually

p = Project.find_by_full_path('<project_path>')
m = project.merge_requests.find_by(iid: )
u = User.find_by_username('<username>'), u).execute(m)


Cancel stuck pending pipelines

For more information, see the confidential issue

Ci::Pipeline.where(project_id: 'pending').count
Ci::Pipeline.where(project_id: 'pending').each {|p| p.cancel if p.stuck?}
Ci::Pipeline.where(project_id: 'pending').count

Remove artifacts more than a week old

This section has been moved to the job artifacts troubleshooting documentation.

Find reason failure (for when build trace is empty) (Introduced in 10.3.0)


build = Ci::Build.find(78420)


build.dependencies.each do |d| { puts "status: #{d.status}, finished at: #{d.finished_at},
  completed: #{d.complete?}, artifacts_expired: #{d.artifacts_expired?}, erased: #{d.erased?}" }

Try CI service

p = Project.find_by_full_path('<project_path>')
m = project.merge_requests.find_by(iid: )

Validate the .gitlab-ci.yml

project = Project.find_by_full_path 'group/project'
content = project.repository.gitlab_ci_yml_for(project.repository.root_ref_sha) project,  current_user: User.first).validate(content)

Disable AutoDevOps on Existing Projects

Project.all.each do |p|

Obtain runners registration token


Run pipeline schedules manually

You can run pipeline schedules manually through the Rails console to reveal any errors that are usually not visible.

# schedule_id can be obtained from Edit Pipeline Schedule page
schedule = Ci::PipelineSchedule.find_by(id: <schedule_id>)

# Select the user that you want to run the schedule for
user = User.find_by_username('<username>')

# Run the schedule
ps =, user, ref: schedule.ref).execute!(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule)


See current license information

# License information (name, company, email address)

# Plan:

# Uploaded:

# Started:

# Expires at:

# Is this a trial license?

Check if a project feature is available on the instance

Features listed in


Check if a project feature is available in a project

Features listed in license.rb.

p = Project.find_by_full_path('<group>/<project>')

Add a license through the console

key = "<key>"
license = key)
License.current # check to make sure it applied


From Zendesk ticket #91083 (internal)

Poll Unicorn requests by seconds

require 'rubygems'
require 'unicorn'

# Usage for this program
def usage
  puts "ruby unicorn_status.rb <path to unix socket> <poll interval in seconds>"
  puts "Polls the given Unix socket every interval in seconds. Will not allow you to drop below 3 second poll intervals."
  puts "Example: /opt/gitlab/embedded/bin/ruby poll_unicorn.rb /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket 10"

# Look for required args. Throw usage and exit if they don't exist.
if ARGV.count < 2
  exit 1

# Get the socket and threshold values.
socket = ARGV[0]
threshold = (ARGV[1]).to_i

# Check threshold - is it less than 3? If so, set to 3 seconds. Safety first!
if threshold.to_i < 3
  threshold = 3

# Check - does that socket exist?
unless File.exist?(socket)
  puts "Socket file not found: #{socket}"
  exit 1

# Poll the given socket every THRESHOLD seconds as specified above.
puts "Running infinite loop. Use CTRL+C to exit."
puts "------------------------------------------"
loop do
  Raindrops::Linux.unix_listener_stats([socket]).each do |addr, stats|
    puts + " Active: " + + " Queued: " + stats.queued.to_s
  sleep threshold


Registry Disk Space Usage by Project

As a GitLab administrator, you may need to reduce disk space consumption. A common culprit is Docker Registry images that are no longer in use. To find the storage broken down by each project, run the following in the GitLab Rails console:

projects_and_size = [["project_id", "creator_id", "registry_size_bytes", "project path"]]
# You need to specify the projects that you want to look through. You can get these in any manner.
projects = Project.last(100)

projects.each do |p|
   project_total_size = 0
   container_repositories = p.container_repositories

   container_repositories.each do |c|
       c.tags.each do |t|
          project_total_size = project_total_size + t.total_size unless t.total_size.nil?

   if project_total_size > 0
      projects_and_size << [p.project_id,, project_total_size, p.full_path]

# projects_and_size is filled out now
# maybe print it as comma separated output?
projects_and_size.each do |ps|
   puts "%s,%s,%s,%s" % ps

Run the Cleanup policy now

Find this content in the Container Registry troubleshooting docs.


This content has been moved to the Troubleshooting Sidekiq docs.


Connect to Redis (omnibus)

/opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket


Get information about LFS objects and associated project

o=LfsObject.find_by(oid: "<oid>")

You can then delete these records from the database with:


You would also want to combine this with deleting the LFS file in the LFS storage area on disk. It remains to be seen exactly how or whether the deletion is useful, however.

Decryption Problems

Bad Decrypt Script (for encrypted variables)

This content has been converted to a Rake task, see the Doctor Rake tasks docs.

As an example of repairing, if ProjectImportData Bad count: is detected and the decision is made to delete the encrypted credentials to allow manual reentry:

  # Find the ids of the corrupt ProjectImportData objects
  total = 0
  bad = []
  ProjectImportData.find_each do |data|
      total += 1
    rescue => e
      bad <<

  puts "Bad count: #{bad.count} / #{total}"

  # See the bad ProjectImportData ids

  # Remove the corrupted credentials
  import_data = ProjectImportData.where(id: bad)
  import_data.each do |data|
    data.update_columns({ encrypted_credentials: nil, encrypted_credentials_iv: nil, encrypted_credentials_salt: nil})

If User OTP Secret Bad count: is detected. For each user listed disable/enable two-factor authentication.

The following script will search in some of the tables for encrypted tokens that are causing decryption errors, and update or reset as needed:

wget -O /tmp/encrypted-tokens.rb
gitlab-rails runner /tmp/encrypted-tokens.rb

Decrypt Script for encrypted tokens

This content has been converted to a Rake task, see the Doctor Rake tasks docs.



Find failed artifacts


Download artifact, <artifact_id>).execute

Get a count of the synced artifacts


Find ID of synced artifacts that are missing on primary


Repository verification failures

Get the number of verification failed repositories


Find the verification failed repositories


Find repositories that failed to sync


Resync repositories

Queue up all repositories for resync. Sidekiq will handle each sync

Geo::ProjectRegistry.update_all(resync_repository: true, resync_wiki: true)

Sync individual repository now

project = Project.find_by_full_path('<group/project>')

Blob types newer than uploads/artifacts/LFS

  • Packages::PackageFile
  • Terraform::StateVersion
  • MergeRequestDiff

Packages::PackageFile is used in the following examples, but things generally work the same for the other Blob types.

The Replicator

The main kinds of classes are Registry, Model, and Replicator. If you have an instance of one of these classes, you can get the others. The Registry and Model mostly manage PostgreSQL DB state. The Replicator knows how to replicate/verify (or it can call a service to do it):

model_record = Packages::PackageFile.last
model_record.replicator.registry.replicator.model_record # just showing that these methods exist

Replicate a package file, synchronously, given an ID

model_record = Packages::PackageFile.find(id)

Replicate a package file, synchronously, given a registry ID

registry = Geo::PackageFileRegistry.find(registry_id)

Repository types newer than project/wiki repositories

  • SnippetRepository
  • GroupWikiRepository

SnippetRepository is used in the examples below, but things generally work the same for the other Repository types.

The Replicator

The main kinds of classes are Registry, Model, and Replicator. If you have an instance of one of these classes, you can get the others. The Registry and Model mostly manage PostgreSQL DB state. The Replicator knows how to replicate/verify (or it can call a service to do it).

model_record = SnippetRepository.last
model_record.replicator.registry.replicator.model_record # just showing that these methods exist

Replicate a snippet repository, synchronously, given an ID

model_record = SnippetRepository.find(id)

Replicate a snippet repository, synchronously, given a registry ID

registry = Geo::SnippetRepositoryRegistry.find(registry_id)

Generate usage ping

Generate or get the cached usage ping


Generate a fresh new usage ping

This will also refresh the cached usage ping displayed in the admin area

Gitlab::UsageData.to_json(force_refresh: true)

Generate and print

Generates usage ping data in JSON format.

rake gitlab:usage_data:generate

Generate and send usage ping

Prints the metrics saved in conversational_development_index_metrics.

rake gitlab:usage_data:generate_and_send


Configuration attributes

Open the rails console (gitlab rails c) and run the following command to see all the available attributes:


Among other attributes, in the output you will notice that all the settings available in the Elasticsearch Integration page, like: elasticsearch_indexing, elasticsearch_url, elasticsearch_replicas, elasticsearch_pause_indexing, etc.

Setting attributes

You can then set anyone of Elasticsearch integration settings by issuing a command similar to:

ApplicationSetting.last.update_attributes(elasticsearch_url: '<your ES URL and port>')


ApplicationSetting.last.update_attributes(elasticsearch_indexing: false)

Getting attributes

You can then check if the settings have been set in the Elasticsearch Integration page or in the rails console by issuing: