diff --git a/app/models/project.rb b/app/models/project.rb
index 19bbe65b01d6b6281eae3dc5b770e5393e463aea..e0ffa7e7af7f6a2e8cfc31cfc534006cbdfffd8a 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -926,7 +926,7 @@ def rename_repo
       Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present"
 
       # we currently doesn't support renaming repository if it contains tags in container registry
-      raise Exception.new('Project cannot be renamed, because tags are present in its container registry')
+      raise StandardError.new('Project cannot be renamed, because tags are present in its container registry')
     end
 
     if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
@@ -953,7 +953,7 @@ def rename_repo
 
       # if we cannot move namespace directory we should rollback
       # db changes in order to prevent out of sync between db and fs
-      raise Exception.new('repository cannot be renamed')
+      raise StandardError.new('repository cannot be renamed')
     end
 
     Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
diff --git a/changelogs/unreleased/dz-rename-reserved-project-names.yml b/changelogs/unreleased/dz-rename-reserved-project-names.yml
new file mode 100644
index 0000000000000000000000000000000000000000..30bcc1a0396c4bb358d367161895b80a6bec23c9
--- /dev/null
+++ b/changelogs/unreleased/dz-rename-reserved-project-names.yml
@@ -0,0 +1,4 @@
+---
+title: Rename projects wth reserved names
+merge_request: 8234
+author:
diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7f7c2424a5cad6bda842748903432003024039fd
--- /dev/null
+++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
@@ -0,0 +1,135 @@
+require 'thread'
+
+class RenameReservedProjectNames < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+  include Gitlab::ShellAdapter
+
+  DOWNTIME = false
+
+  THREAD_COUNT = 8
+
+  KNOWN_PATHS = %w(.well-known
+                   all
+                   assets
+                   blame
+                   blob
+                   commits
+                   create
+                   create_dir
+                   edit
+                   files
+                   files
+                   find_file
+                   groups
+                   hooks
+                   issues
+                   logs_tree
+                   merge_requests
+                   new
+                   new
+                   preview
+                   profile
+                   projects
+                   public
+                   raw
+                   repository
+                   robots.txt
+                   s
+                   snippets
+                   teams
+                   tree
+                   u
+                   unsubscribes
+                   update
+                   users
+                   wikis)
+
+  def up
+    queues = Array.new(THREAD_COUNT) { Queue.new }
+    start = false
+
+    threads = Array.new(THREAD_COUNT) do |index|
+      Thread.new do
+        queue = queues[index]
+
+        # Wait until we have input to process.
+        until start; end
+
+        rename_projects(queue.pop) until queue.empty?
+      end
+    end
+
+    enum = queues.each
+
+    reserved_projects.each_slice(100) do |slice|
+      begin
+        queue = enum.next
+      rescue StopIteration
+        enum.rewind
+        retry
+      end
+
+      queue << slice
+    end
+
+    start = true
+
+    threads.each(&:join)
+  end
+
+  def down
+    # nothing to do here
+  end
+
+  private
+
+  def reserved_projects
+    Project.unscoped.
+      includes(:namespace).
+      where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)').
+      where('projects.path' => KNOWN_PATHS)
+  end
+
+  def route_exists?(full_path)
+    quoted_path = ActiveRecord::Base.connection.quote_string(full_path)
+
+    ActiveRecord::Base.connection.
+      select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
+  end
+
+  # Adds number to the end of the path that is not taken by other route
+  def rename_path(namespace_path, path_was)
+    counter = 0
+    path = "#{path_was}#{counter}"
+
+    while route_exists?("#{namespace_path}/#{path}")
+      counter += 1
+      path = "#{path_was}#{counter}"
+    end
+
+    path
+  end
+
+  def rename_projects(projects)
+    projects.each do |project|
+      id = project.id
+      path_was = project.path
+      namespace_path = project.namespace.path
+      path = rename_path(namespace_path, path_was)
+
+      begin
+        # Because project path update is quite complex operation we can't safely
+        # copy-paste all code from GitLab. As exception we use Rails code here
+        project.rename_repo if rename_project_row(project, path)
+      rescue Exception => e # rubocop: disable Lint/RescueException
+        Rails.logger.error "Exception when renaming project #{id}: #{e.message}"
+      end
+    end
+  end
+
+  def rename_project_row(project, path)
+    project.respond_to?(:update_attributes) &&
+      project.update_attributes(path: path) &&
+      project.respond_to?(:rename_repo)
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 2198dcee2cda857e062cef218849a82381795138..e1e72bdae8f40a424425d4c59adef0b1b6d639e6 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1306,4 +1306,4 @@
   add_foreign_key "subscriptions", "projects", on_delete: :cascade
   add_foreign_key "trending_projects", "projects", on_delete: :cascade
   add_foreign_key "u2f_registrations", "users"
-end
+end
\ No newline at end of file
diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4fb7ed36884ad6bce04de27c323bd5ae97b52fcc
--- /dev/null
+++ b/spec/migrations/rename_reserved_project_names_spec.rb
@@ -0,0 +1,47 @@
+# encoding: utf-8
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20161221153951_rename_reserved_project_names.rb')
+
+# This migration uses multiple threads, and thus different transactions. This
+# means data created in this spec may not be visible to some threads. To work
+# around this we use the TRUNCATE cleaning strategy.
+describe RenameReservedProjectNames, truncate: true do
+  let(:migration) { described_class.new }
+  let!(:project) { create(:empty_project) }
+
+  before do
+    project.path = 'projects'
+    project.save!(validate: false)
+  end
+
+  describe '#up' do
+    context 'when project repository exists' do
+      before { project.create_repository }
+
+      context 'when no exception is raised' do
+        it 'renames project with reserved names' do
+          migration.up
+
+          expect(project.reload.path).to eq('projects0')
+        end
+      end
+
+      context 'when exception is raised during rename' do
+        before do
+          allow(project).to receive(:rename_repo).and_raise(StandardError)
+        end
+
+        it 'captures exception from project rename' do
+          expect { migration.up }.not_to raise_error
+        end
+      end
+    end
+
+    context 'when project repository does not exist' do
+      it 'does not raise error' do
+        expect { migration.up }.not_to raise_error
+      end
+    end
+  end
+end