diff --git a/CHANGELOG b/CHANGELOG
index d1a0e4671471718605666be7c72f404260b932ed..0a21462ebdbc78b5d62d26c8d55e78cadbb8ee20 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -42,6 +42,7 @@ v 8.4.1
   - Fix redirect loop during import
   - Fix diff highlighting for all syntax themes
   - Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
+  - Delete project and associations in a background worker
 
 v 8.4.0
   - Allow LDAP users to change their email if it was not set by the LDAP server
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 935f7d75c6af49435f5a3d7ce97a4f7676052622..4df5095bd94735cf45ae140b03fc92e1a525833e 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -93,6 +93,10 @@ class ProjectsController < ApplicationController
       return
     end
 
+    if @project.pending_delete?
+      flash[:alert] = "Project queued for delete."
+    end
+
     respond_to do |format|
       format.html do
         if @project.repository_exists?
@@ -120,8 +124,8 @@ class ProjectsController < ApplicationController
   def destroy
     return access_denied! unless can?(current_user, :remove_project, @project)
 
-    ::Projects::DestroyService.new(@project, current_user, {}).execute
-    flash[:alert] = "Project '#{@project.name}' was deleted."
+    ::Projects::DestroyService.new(@project, current_user, {}).pending_delete!
+    flash[:alert] = "Project '#{@project.name}' will be deleted."
 
     redirect_to dashboard_projects_path
   rescue Projects::DestroyService::DestroyError => ex
diff --git a/app/models/project.rb b/app/models/project.rb
index 238932f59a767b7ff335ffeef9680c9f5f762a99..043f08b9a13a36f4245f9a8aa0f6daeb934b4614 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -36,6 +36,7 @@
 #  build_coverage_regex   :string
 #  build_allow_git_fetch  :boolean          default(TRUE), not null
 #  build_timeout          :integer          default(3600), not null
+#  pending_delete         :boolean
 #
 
 require 'carrierwave/orm/activerecord'
diff --git a/app/services/delete_user_service.rb b/app/services/delete_user_service.rb
index e622fd5ea5d1e5f95bbb40a3334bd718eaa33567..173e50c9206ddd31f317d00ed88cfd9c2738c5fe 100644
--- a/app/services/delete_user_service.rb
+++ b/app/services/delete_user_service.rb
@@ -13,7 +13,7 @@ class DeleteUserService
       user.personal_projects.each do |project|
         # Skip repository removal because we remove directory with namespace
         # that contain all this repositories
-        ::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
+        ::Projects::DestroyService.new(project, current_user, skip_repo: true).pending_delete!
       end
 
       user.destroy
diff --git a/app/services/destroy_group_service.rb b/app/services/destroy_group_service.rb
index d929a6762933d0d928eb1bc170186a3dd4610dad..9189de390a2bfae5d723f0c386045dd11aa5f96b 100644
--- a/app/services/destroy_group_service.rb
+++ b/app/services/destroy_group_service.rb
@@ -9,7 +9,7 @@ class DestroyGroupService
     @group.projects.each do |project|
       # Skip repository removal because we remove directory with namespace
       # that contain all this repositories
-      ::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
+      ::Projects::DestroyService.new(project, current_user, skip_repo: true).pending_delete!
     end
 
     @group.destroy
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 28872c89259d4f939d31d071cdab00f5f161e71b..294157b4f0e4edbeaf3c231e0c8e7e72d0e84358 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -6,6 +6,12 @@ module Projects
 
     DELETED_FLAG = '+deleted'
 
+    def pending_delete!
+      project.update_attribute(:pending_delete, true)
+
+      ProjectDestroyWorker.perform_in(1.minute, project.id, current_user.id, params)
+    end
+
     def execute
       return false unless can?(current_user, :remove_project, project)
 
diff --git a/app/workers/project_destroy_worker.rb b/app/workers/project_destroy_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d06e448029288aec11b98943158c5b45be0a0aa8
--- /dev/null
+++ b/app/workers/project_destroy_worker.rb
@@ -0,0 +1,17 @@
+class ProjectDestroyWorker
+  include Sidekiq::Worker
+
+  sidekiq_options queue: :default
+
+  def perform(project_id, user_id, params)
+    begin
+      project = Project.find(project_id)
+    rescue ActiveRecord::RecordNotFound
+      return
+    end
+
+    user = User.find(user_id)
+
+    ::Projects::DestroyService.new(project, user, params).execute
+  end
+end
diff --git a/db/migrate/20160122185421_add_pending_delete_to_project.rb b/db/migrate/20160122185421_add_pending_delete_to_project.rb
new file mode 100644
index 0000000000000000000000000000000000000000..046a5d8fc3214ff219c997fed7b34b52be784f6f
--- /dev/null
+++ b/db/migrate/20160122185421_add_pending_delete_to_project.rb
@@ -0,0 +1,5 @@
+class AddPendingDeleteToProject < ActiveRecord::Migration
+  def change
+    add_column :projects, :pending_delete, :boolean, default: false
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 96b5c3af1e55afa98f1ad7bcad1251b08f060d25..2ad2c23fba52e8d3eed6842b83830fa5ac49239d 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -677,6 +677,7 @@ ActiveRecord::Schema.define(version: 20160128233227) do
     t.string   "build_coverage_regex"
     t.boolean  "build_allow_git_fetch",  default: true,     null: false
     t.integer  "build_timeout",          default: 3600,     null: false
+    t.boolean  "pending_delete",         default: false
   end
 
   add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 71bb342f8448cacd216acf55fb80a0a56265242b..1f991e600e38e2486a5b3b5e879bdbfc0a2593c2 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -187,7 +187,7 @@ module API
         else
           present @forked_project, with: Entities::Project,
                                    user_can_admin_project: can?(current_user, :admin_project, @forked_project)
-         end
+        end
       end
 
       # Update an existing project
@@ -246,7 +246,7 @@ module API
       #   DELETE /projects/:id
       delete ":id" do
         authorize! :remove_project, user_project
-        ::Projects::DestroyService.new(user_project, current_user, {}).execute
+        ::Projects::DestroyService.new(user_project, current_user, {}).pending_delete!
       end
 
       # Mark this project as forked from another
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 138b87a9a061c642f6ffa2adef4e136bbd04a874..fd1513cab1b04071017dae48922182db63b4736c 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -36,7 +36,7 @@ describe SystemHook, models: true do
     it "project_destroy hook" do
       user = create(:user)
       project = create(:empty_project, namespace: user.namespace)
-      Projects::DestroyService.new(project, user, {}).execute
+      Projects::DestroyService.new(project, user, {}).pending_delete!
       expect(WebMock).to have_requested(:post, @system_hook.url).with(
         body: /project_destroy/,
         headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
@@ -65,7 +65,7 @@ describe SystemHook, models: true do
       project = create(:project)
       project.team << [user, :master]
       expect(WebMock).to have_requested(:post, @system_hook.url).with(
-        body: /user_add_to_team/, 
+        body: /user_add_to_team/,
         headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
       ).once
     end
@@ -76,7 +76,7 @@ describe SystemHook, models: true do
       project.team << [user, :master]
       project.project_members.destroy_all
       expect(WebMock).to have_requested(:post, @system_hook.url).with(
-        body: /user_remove_from_team/, 
+        body: /user_remove_from_team/,
         headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
       ).once
     end