diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index a9f482c8787b0edad09127fcaa472fd8133a86b7..d0f5071d2cc21195e962f8a5341f8d568bb195c0 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -8,8 +8,9 @@ class Projects::BadgesController < Projects::ApplicationController
 
     respond_to do |format|
       format.html { render_404 }
+
       format.svg do
-        send_data(badge.data, type: badge.type, disposition: 'inline')
+        render 'badge', locals: { badge: badge.template }
       end
     end
   end
diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb
index 85ba706e5cd050eeb19e074a8141611d344e765a..75dd3648e4528004eb36bc99cc125f7e407848c2 100644
--- a/app/controllers/projects/pipelines_settings_controller.rb
+++ b/app/controllers/projects/pipelines_settings_controller.rb
@@ -3,7 +3,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
 
   def show
     @ref = params[:ref] || @project.default_branch || 'master'
-    @build_badge = Gitlab::Badge::Build.new(@project, @ref)
+    @build_badge = Gitlab::Badge::Build.new(@project, @ref).metadata
   end
 
   def update
diff --git a/app/views/projects/badges/badge.svg.erb b/app/views/projects/badges/badge.svg.erb
new file mode 100644
index 0000000000000000000000000000000000000000..a5fef4fc56faf2a2ff341969936dcd214c04853b
--- /dev/null
+++ b/app/views/projects/badges/badge.svg.erb
@@ -0,0 +1,36 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="<%= badge.width %>" height="20">
+  <linearGradient id="b" x2="0" y2="100%">
+    <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
+    <stop offset="1" stop-opacity=".1"/>
+  </linearGradient>
+
+  <mask id="a">
+    <rect width="<%= badge.width %>" height="20" rx="3" fill="#fff"/>
+  </mask>
+
+  <g mask="url(#a)">
+    <path fill="<%= badge.key_color %>"
+          d="M0 0 h<%= badge.key_width %> v20 H0 z"/>
+    <path fill="<%= badge.value_color %>"
+          d="M<%= badge.key_width %> 0 h<%= badge.value_width %> v20 H<%= badge.key_width %> z"/>
+    <path fill="url(#b)"
+          d="M0 0 h<%= badge.width %> v20 H0 z"/>
+  </g>
+
+  <g fill="#fff" text-anchor="middle">
+    <g font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
+      <text x="<%= badge.key_text_anchor %>" y="15" fill="#010101" fill-opacity=".3">
+        <%= badge.key_text %>
+      </text>
+      <text x="<%= badge.key_text_anchor %>" y="14">
+        <%= badge.key_text %>
+      </text>
+      <text x="<%= badge.value_text_anchor %>" y="15" fill="#010101" fill-opacity=".3">
+        <%= badge.value_text %>
+      </text>
+      <text x="<%= badge.value_text_anchor %>" y="14">
+        <%= badge.value_text %>
+      </text>
+    </g>
+  </g>
+</svg>
diff --git a/features/steps/project/badges/build.rb b/features/steps/project/badges/build.rb
index 66a48a176e58703e062660a7cd51e2f2f6957e47..96c59322f9b2f59d7a0231a836c2d5bb751f996b 100644
--- a/features/steps/project/badges/build.rb
+++ b/features/steps/project/badges/build.rb
@@ -26,7 +26,7 @@ class Spinach::Features::ProjectBadgesBuild < Spinach::FeatureSteps
 
   def expect_badge(status)
     svg = Nokogiri::XML.parse(page.body)
-    expect(page.response_headers).to include('Content-Type' => 'image/svg+xml')
+    expect(page.response_headers['Content-Type']).to include('image/svg+xml')
     expect(svg.at(%Q{text:contains("#{status}")})).to be_truthy
   end
 end
diff --git a/lib/gitlab/badge/build.rb b/lib/gitlab/badge/build.rb
index e5e9fab3f5c8c8a7cff53de608a79c95ff199678..1de721a2269633a94f3e0a4071ddebc8da5ed60a 100644
--- a/lib/gitlab/badge/build.rb
+++ b/lib/gitlab/badge/build.rb
@@ -4,42 +4,26 @@ module Gitlab
     # Build badge
     #
     class Build
-      include Gitlab::Application.routes.url_helpers
-      include ActionView::Helpers::AssetTagHelper
-      include ActionView::Helpers::UrlHelper
+      delegate :key_text, :value_text, to: :template
 
       def initialize(project, ref)
-        @project, @ref = project, ref
-        @image = ::Ci::ImageForBuildService.new.execute(project, ref: ref)
+        @project = project
+        @ref = ref
+        @sha = @project.commit(@ref).try(:sha)
       end
 
-      def type
-        'image/svg+xml'
+      def status
+        @project.pipelines
+          .where(sha: @sha, ref: @ref)
+          .status || 'unknown'
       end
 
-      def data
-        File.read(@image[:path])
+      def metadata
+        @metadata ||= Build::Metadata.new(@project, @ref)
       end
 
-      def to_s
-        @image[:name].sub(/\.svg$/, '')
-      end
-
-      def to_html
-        link_to(image_tag(image_url, alt: 'build status'), link_url)
-      end
-
-      def to_markdown
-        "[![build status](#{image_url})](#{link_url})"
-      end
-
-      def image_url
-        build_namespace_project_badges_url(@project.namespace,
-                                           @project, @ref, format: :svg)
-      end
-
-      def link_url
-        namespace_project_commits_url(@project.namespace, @project, id: @ref)
+      def template
+        @template ||= Build::Template.new(status)
       end
     end
   end
diff --git a/lib/gitlab/badge/build/metadata.rb b/lib/gitlab/badge/build/metadata.rb
new file mode 100644
index 0000000000000000000000000000000000000000..553ef8d7b1616964e379804b248d18310e014314
--- /dev/null
+++ b/lib/gitlab/badge/build/metadata.rb
@@ -0,0 +1,36 @@
+module Gitlab
+  module Badge
+    class Build
+      ##
+      # Class that describes build badge metadata
+      #
+      class Metadata
+        include Gitlab::Application.routes.url_helpers
+        include ActionView::Helpers::AssetTagHelper
+        include ActionView::Helpers::UrlHelper
+
+        def initialize(project, ref)
+          @project = project
+          @ref = ref
+        end
+
+        def to_html
+          link_to(image_tag(image_url, alt: 'build status'), link_url)
+        end
+
+        def to_markdown
+          "[![build status](#{image_url})](#{link_url})"
+        end
+
+        def image_url
+          build_namespace_project_badges_url(@project.namespace,
+                                             @project, @ref, format: :svg)
+        end
+
+        def link_url
+          namespace_project_commits_url(@project.namespace, @project, id: @ref)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/badge/build/template.rb b/lib/gitlab/badge/build/template.rb
new file mode 100644
index 0000000000000000000000000000000000000000..deba3b669b3af96421779373989e812a26d0fb6e
--- /dev/null
+++ b/lib/gitlab/badge/build/template.rb
@@ -0,0 +1,63 @@
+module Gitlab
+  module Badge
+    class Build
+      ##
+      # Class that represents a build badge template.
+      #
+      # Template object will be passed to badge.svg.erb template.
+      #
+      class Template
+        STATUS_COLOR = {
+          success: '#4c1',
+          failed: '#e05d44',
+          running: '#dfb317',
+          pending: '#dfb317',
+          canceled: '#9f9f9f',
+          skipped: '#9f9f9f',
+          unknown: '#9f9f9f'
+        }
+
+        def initialize(status)
+          @status = status
+        end
+
+        def key_text
+          'build'
+        end
+
+        def value_text
+          @status
+        end
+
+        def key_width
+          38
+        end
+
+        def value_width
+          54
+        end
+
+        def key_color
+          '#555'
+        end
+
+        def value_color
+          STATUS_COLOR[@status.to_sym] ||
+            STATUS_COLOR[:unknown]
+        end
+
+        def key_text_anchor
+          key_width / 2
+        end
+
+        def value_text_anchor
+          key_width + (value_width / 2)
+        end
+
+        def width
+          key_width + value_width
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/badge/build/metadata_spec.rb b/spec/lib/gitlab/badge/build/metadata_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ad5388215c27cf5c2d0bcc0e70aa8823ca270265
--- /dev/null
+++ b/spec/lib/gitlab/badge/build/metadata_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Gitlab::Badge::Build::Metadata do
+  let(:project) { create(:project) }
+  let(:branch) { 'master' }
+  let(:badge) { described_class.new(project, branch) }
+
+  describe '#to_html' do
+    let(:html) { Nokogiri::HTML.parse(badge.to_html) }
+    let(:a_href) { html.at('a') }
+
+    it 'points to link' do
+      expect(a_href[:href]).to eq badge.link_url
+    end
+
+    it 'contains clickable image' do
+      expect(a_href.children.first.name).to eq 'img'
+    end
+  end
+
+  describe '#to_markdown' do
+    subject { badge.to_markdown }
+
+    it { is_expected.to include badge.image_url }
+    it { is_expected.to include badge.link_url }
+  end
+
+  describe '#image_url' do
+    subject { badge.image_url }
+    it { is_expected.to include "badges/#{branch}/build.svg" }
+  end
+
+  describe '#link_url' do
+    subject { badge.link_url }
+    it { is_expected.to include "commits/#{branch}" }
+  end
+end
diff --git a/spec/lib/gitlab/badge/build/template_spec.rb b/spec/lib/gitlab/badge/build/template_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..86dead3c54e1c2089fc7f6bd186a81d35be99bc4
--- /dev/null
+++ b/spec/lib/gitlab/badge/build/template_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe Gitlab::Badge::Build::Template do
+  let(:status) { 'success' }
+  let(:template) { described_class.new(status) }
+
+  describe '#key_text' do
+    it 'is always says build' do
+      expect(template.key_text).to eq 'build'
+    end
+  end
+
+  describe '#value_text' do
+    it 'is status value' do
+      expect(template.value_text).to eq 'success'
+    end
+  end
+
+  describe 'widths and text anchors' do
+    it 'has fixed width and text anchors' do
+      expect(template.width).to eq 92
+      expect(template.key_width).to eq 38
+      expect(template.value_width).to eq 54
+      expect(template.key_text_anchor).to eq 19
+      expect(template.value_text_anchor).to eq 65
+    end
+  end
+
+  describe '#key_color' do
+    it 'is always the same' do
+      expect(template.key_color).to eq '#555'
+    end
+  end
+
+  describe '#value_color' do
+    context 'when status is success' do
+      let(:status) { 'success' }
+
+      it 'has expected color' do
+        expect(template.value_color).to eq '#4c1'
+      end
+    end
+
+    context 'when status is failed' do
+      let(:status) { 'failed' }
+
+      it 'has expected color' do
+        expect(template.value_color).to eq '#e05d44'
+      end
+    end
+
+    context 'when status is running' do
+      let(:status) { 'running' }
+
+      it 'has expected color' do
+        expect(template.value_color).to eq '#dfb317'
+      end
+    end
+
+    context 'when status is unknown' do
+      let(:status) { 'unknown' }
+
+      it 'has expected color' do
+        expect(template.value_color).to eq '#9f9f9f'
+      end
+    end
+
+    context 'when status does not match any known statuses' do
+      let(:status) { 'invalid status' }
+
+      it 'has expected color' do
+        expect(template.value_color).to eq '#9f9f9f'
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build_spec.rb
index f3b522a02f52d173e5fa06f0a8e09775c5dca69c..ef9d9e7fef431dba04825ae60a6fb6b85518b836 100644
--- a/spec/lib/gitlab/badge/build_spec.rb
+++ b/spec/lib/gitlab/badge/build_spec.rb
@@ -6,39 +6,17 @@ describe Gitlab::Badge::Build do
   let(:branch) { 'master' }
   let(:badge) { described_class.new(project, branch) }
 
-  describe '#type' do
-    subject { badge.type }
-    it { is_expected.to eq 'image/svg+xml' }
-  end
-
-  describe '#to_html' do
-    let(:html) { Nokogiri::HTML.parse(badge.to_html) }
-    let(:a_href) { html.at('a') }
-
-    it 'points to link' do
-      expect(a_href[:href]).to eq badge.link_url
-    end
-
-    it 'contains clickable image' do
-      expect(a_href.children.first.name).to eq 'img'
+  describe '#metadata' do
+    it 'returns badge metadata' do
+      expect(badge.metadata.image_url)
+        .to include 'badges/master/build.svg'
     end
   end
 
-  describe '#to_markdown' do
-    subject { badge.to_markdown }
-
-    it { is_expected.to include badge.image_url }
-    it { is_expected.to include badge.link_url }
-  end
-
-  describe '#image_url' do
-    subject { badge.image_url }
-    it { is_expected.to include "badges/#{branch}/build.svg" }
-  end
-
-  describe '#link_url' do
-    subject { badge.link_url }
-    it { is_expected.to include "commits/#{branch}" }
+  describe '#key_text' do
+    it 'always says build' do
+      expect(badge.key_text).to eq 'build'
+    end
   end
 
   context 'build exists' do
@@ -47,16 +25,15 @@ describe Gitlab::Badge::Build do
     context 'build success' do
       before { build.success! }
 
-      describe '#to_s' do
-        subject { badge.to_s }
-        it { is_expected.to eq 'build-success' }
+      describe '#status' do
+        it 'is successful' do
+          expect(badge.status).to eq 'success'
+        end
       end
 
-      describe '#data' do
-        let(:data) { badge.data }
-
-        it 'contains information about success' do
-          expect(status_node(data, 'success')).to be_truthy
+      describe '#value_text' do
+        it 'returns correct value text' do
+          expect(badge.value_text).to eq 'success'
         end
       end
     end
@@ -64,47 +41,57 @@ describe Gitlab::Badge::Build do
     context 'build failed' do
       before { build.drop! }
 
-      describe '#to_s' do
-        subject { badge.to_s }
-        it { is_expected.to eq 'build-failed' }
+      describe '#status' do
+        it 'failed' do
+          expect(badge.status).to eq 'failed'
+        end
       end
 
-      describe '#data' do
-        let(:data) { badge.data }
-
-        it 'contains information about failure' do
-          expect(status_node(data, 'failed')).to be_truthy
+      describe '#value_text' do
+        it 'has correct value text' do
+          expect(badge.value_text).to eq 'failed'
         end
       end
     end
-  end
 
-  context 'build does not exist' do
-    describe '#to_s' do
-      subject { badge.to_s }
-      it { is_expected.to eq 'build-unknown' }
+    context 'when outdated pipeline for given ref exists' do
+      before do
+        build.success!
+
+        old_build = create_build(project, '11eeffdd', branch)
+        old_build.drop!
+      end
+
+      it 'does not take outdated pipeline into account' do
+        expect(badge.status).to eq 'success'
+      end
     end
 
-    describe '#data' do
-      let(:data) { badge.data }
+    context 'when multiple pipelines exist for given sha' do
+      before do
+        build.drop!
+
+        new_build = create_build(project, sha, branch)
+        new_build.success!
+      end
 
-      it 'contains infromation about unknown build' do
-        expect(status_node(data, 'unknown')).to be_truthy
+      it 'reports the compound status' do
+        expect(badge.status).to eq 'failed'
       end
     end
   end
 
-  context 'when outdated pipeline for given ref exists' do
-    before do
-      build = create_build(project, sha, branch)
-      build.success!
-
-      old_build = create_build(project, '11eeffdd', branch)
-      old_build.drop!
+  context 'build does not exist' do
+    describe '#status' do
+      it 'is unknown' do
+        expect(badge.status).to eq 'unknown'
+      end
     end
 
-    it 'does not take outdated pipeline into account' do
-      expect(badge.to_s).to eq 'build-success'
+    describe '#value_text' do
+      it 'has correct value text' do
+        expect(badge.value_text).to eq 'unknown'
+      end
     end
   end
 
@@ -115,9 +102,4 @@ describe Gitlab::Badge::Build do
 
     create(:ci_build, pipeline: pipeline, stage: 'notify')
   end
-
-  def status_node(data, status)
-    xml = Nokogiri::XML.parse(data)
-    xml.at(%Q{text:contains("#{status}")})
-  end
 end