diff --git a/app/controllers/unicorn_test_controller.rb b/app/controllers/unicorn_test_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b7a1a046be0b785d7ec7b9b4cb0b1897ed381acd
--- /dev/null
+++ b/app/controllers/unicorn_test_controller.rb
@@ -0,0 +1,12 @@
+if Rails.env.test?
+  class UnicornTestController < ActionController::Base
+    def pid
+      render plain: Process.pid.to_s
+    end
+  
+    def kill
+      Process.kill(params[:signal], Process.pid)
+      render plain: 'Bye!'
+    end
+  end
+end
diff --git a/config/routes.rb b/config/routes.rb
index 1da226a3b575b54aefbe8343b4c60261e55dabe8..2584981bb040e3448f462fe7516e9fd9b7d40301 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -99,5 +99,7 @@
     end
   end
 
+  draw :test if Rails.env.test?
+
   get '*unmatched_route', to: 'application#route_not_found'
 end
diff --git a/config/routes/test.rb b/config/routes/test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ac477cdbbbc2c69a892df513b05ed6665686569a
--- /dev/null
+++ b/config/routes/test.rb
@@ -0,0 +1,2 @@
+get '/unicorn_test/pid' => 'unicorn_test#pid'
+post '/unicorn_test/kill' => 'unicorn_test#kill'
diff --git a/lib/tasks/brakeman.rake b/lib/tasks/brakeman.rake
index 2301ec9b2288c16b0a32701e40c679a8f107dfcd..99b3168d9eb85c7c43073918f3222c9a410dda1c 100644
--- a/lib/tasks/brakeman.rake
+++ b/lib/tasks/brakeman.rake
@@ -2,7 +2,7 @@ desc 'Security check via brakeman'
 task :brakeman do
   # We get 0 warnings at level 'w3' but we would like to reach 'w2'. Merge
   # requests are welcome!
-  if system(*%w(brakeman --no-progress --skip-files lib/backup/repository.rb -w3 -z))
+  if system(*%w(brakeman --no-progress --skip-files lib/backup/repository.rb,app/controllers/unicorn_test_controller.rb -w3 -z))
     puts 'Security check succeed'
   else
     puts 'Security check failed'
diff --git a/spec/unicorn/unicorn_spec.rb b/spec/unicorn/unicorn_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8518c047a470e573aafad5f185d3eaa931bd444e
--- /dev/null
+++ b/spec/unicorn/unicorn_spec.rb
@@ -0,0 +1,98 @@
+require 'fileutils'
+
+require 'excon'
+
+require 'spec_helper'
+
+describe 'Unicorn' do
+  before(:all) do
+    config_lines = File.read('config/unicorn.rb.example').split("\n")
+
+    # Remove these because they make setup harder.
+    config_lines = config_lines.reject do |line|
+      %w[
+        working_directory
+        worker_processes
+        listen
+        pid
+        stderr_path
+        stdout_path
+      ].any? { |prefix| line.start_with?(prefix) }
+    end
+
+    config_lines << "working_directory '#{Rails.root}'"
+
+    # We want to have exactly 1 worker process because that makes it
+    # predictable which process will handle our requests.
+    config_lines << 'worker_processes 1'
+
+    @socket_path = File.join(Dir.pwd, 'tmp/tests/unicorn.socket')
+    config_lines << "listen '#{@socket_path}'"
+
+    ready_file = 'tmp/tests/unicorn-worker-ready'
+    FileUtils.rm_f(ready_file)
+    after_fork_index = config_lines.index { |l| l.start_with?('after_fork') }
+    config_lines.insert(after_fork_index + 1, "File.write('#{ready_file}', Process.pid)")
+
+    config_path = 'tmp/tests/unicorn.rb'
+    File.write(config_path, config_lines.join("\n") + "\n")
+
+    cmd = %W[unicorn -E test -c #{config_path} #{Rails.root.join('config.ru')}]
+    @unicorn_master_pid = spawn(*cmd)
+    wait_unicorn_boot!(@unicorn_master_pid, ready_file)
+    WebMock.allow_net_connect!
+  end
+
+  %w[SIGQUIT SIGTERM SIGKILL].each do |signal|
+    it "has a worker that self-terminates on signal #{signal}" do
+      response = Excon.get('unix:///unicorn_test/pid', socket: @socket_path)
+      expect(response.status).to eq(200)
+
+      worker_pid = response.body.to_i
+      expect(worker_pid).to be > 0
+
+      begin
+        Excon.post('unix:///unicorn_test/kill', socket: @socket_path, body: "signal=#{signal}")
+      rescue Excon::Error::Socket
+        # The connection may be closed abruptly
+      end
+
+      expect(pid_gone?(worker_pid)).to eq(true)
+    end
+  end
+
+  after(:all) do
+    WebMock.disable_net_connect!(allow_localhost: true)
+    Process.kill('TERM', @unicorn_master_pid)
+  end
+
+  def wait_unicorn_boot!(master_pid, ready_file)
+    # Unicorn should boot in under 60 seconds so 120 seconds seems like a good timeout.
+    timeout = 120
+    timeout.times do
+      return if File.exist?(ready_file)
+      pid = Process.waitpid(master_pid, Process::WNOHANG)
+      raise "unicorn failed to boot: #{$?}" unless pid.nil?
+
+      sleep 1
+    end
+
+    raise "unicorn boot timed out after #{timeout} seconds"
+  end
+
+  def pid_gone?(pid)
+    # Worker termination should take less than a second. That makes 10
+    # seconds a generous timeout.
+    10.times do
+      begin
+        Process.kill(0, pid)
+      rescue Errno::ESRCH
+        return true
+      end
+
+      sleep 1
+    end
+
+    false
+  end
+end