diff --git a/CHANGELOG b/CHANGELOG
index 9490540f1b12bd48581631d45814f93e9628e400..e7c7e65ac4a96b8c9312d2d792bdea4ebfb3d12b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -74,6 +74,7 @@ v 8.4.0 (unreleased)
   - Fix: Creator should be added as a master of the project on creation
   - Added X-GitLab-... headers to emails from CI and Email On Push services (Anton Baklanov)
   - Add IP check against DNSBLs at account sign-up
+  - Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
 
 v 8.3.4
   - Use gitlab-workhorse 0.5.4 (fixes API routing bug)
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index fd0d49de4e438ba654ee160276733cb38768069d..3b594df659d67d27ba1a5a3438521dc84bb4e188 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -135,10 +135,9 @@ thus allowing to fine tune them.
 ### cache
 
 `cache` is used to specify a list of files and directories which should be
-cached between builds. Caches are stored according to the branch/ref and the
-job name. They are not currently shared between different job names or between
-branches/refs, which means that caching will benefit you if you push subsequent
-commits to an existing feature branch.
+cached between builds.
+
+**By default the caching is enabled per-job and per-branch.**
 
 If `cache` is defined outside the scope of the jobs, it means it is set
 globally and all jobs will use its definition.
@@ -152,6 +151,59 @@ cache:
   - binaries/
 ```
 
+#### cache:key
+
+_**Note:** Introduced in GitLab Runner v1.0.0._
+
+The `key` directive allows you to define the affinity of caching
+between jobs, allowing to have a single cache for all jobs,
+cache per-job, cache per-branch or any other way you deem proper.
+
+This allows you to fine tune caching, allowing you to cache data between different jobs or even different branches.
+The `cache:key` variable can use any of the [predefined variables](../variables/README.md):
+
+Example configurations:
+
+To enable per-job caching:
+
+    ```yaml
+    cache:
+      key: "$CI_BUILD_NAME"
+      untracked: true
+    ```
+
+To enable per-branch caching:
+
+    ```yaml
+    cache:
+      key: "$CI_BUILD_REF_NAME"
+      untracked: true
+    ```
+
+To enable per-job and per-branch caching:
+
+    ```yaml
+    cache:
+      key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
+      untracked: true
+    ```
+
+To enable per-branch and per-stage caching:
+
+    ```yaml
+    cache:
+      key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME"
+      untracked: true
+    ```
+
+If you use **Windows Batch** to run your shell scripts you need to replace the `$` with `%`:
+
+    ```yaml
+    cache:
+      key: "%CI_BUILD_STAGE%/%CI_BUILD_REF_NAME%"
+      untracked: true
+    ```
+
 ## Jobs
 
 `.gitlab-ci.yml` allows you to specify an unlimited number of jobs. Each job
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index bcdfd38d292f245b4fe4df8ed7dc93fb4a67dc57..1a3f662811a0a12467cd8cffe520b38bf64a842d 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -115,6 +115,10 @@ module Ci
       end
 
       if @cache
+        if @cache[:key] && !validate_string(@cache[:key])
+          raise ValidationError, "cache:key parameter should be a string"
+        end
+
         if @cache[:untracked] && !validate_boolean(@cache[:untracked])
           raise ValidationError, "cache:untracked parameter should be an boolean"
         end
@@ -198,6 +202,10 @@ module Ci
     end
 
     def validate_job_cache!(name, job)
+      if job[:cache][:key] && !validate_string(job[:cache][:key])
+        raise ValidationError, "#{name} job: cache:key parameter should be a string"
+      end
+
       if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked])
         raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean"
       end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index d15100fc6d8c075de90677c6129d4bb0d24c8fcc..f3394910c5b34d63e0a2fd6dd4b552826dbeac65 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -336,7 +336,7 @@ module Ci
     describe "Caches" do
       it "returns cache when defined globally" do
         config = YAML.dump({
-                             cache: { paths: ["logs/", "binaries/"], untracked: true },
+                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
                              rspec: {
                                script: "rspec"
                              }
@@ -348,13 +348,14 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
           paths: ["logs/", "binaries/"],
           untracked: true,
+          key: 'key',
         )
       end
 
       it "returns cache when defined in a job" do
         config = YAML.dump({
                              rspec: {
-                               cache: { paths: ["logs/", "binaries/"], untracked: true },
+                               cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
                                script: "rspec"
                              }
                            })
@@ -365,15 +366,16 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
           paths: ["logs/", "binaries/"],
           untracked: true,
+          key: 'key',
         )
       end
 
       it "overwrite cache when defined for a job and globally" do
         config = YAML.dump({
-                             cache: { paths: ["logs/", "binaries/"], untracked: true },
+                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'global' },
                              rspec: {
                                script: "rspec",
-                               cache: { paths: ["test/"], untracked: false },
+                               cache: { paths: ["test/"], untracked: false, key: 'local' },
                              }
                            })
 
@@ -383,6 +385,7 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
           paths: ["test/"],
           untracked: false,
+          key: 'local',
         )
       end
     end
@@ -615,6 +618,20 @@ module Ci
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:paths parameter should be an array of strings")
       end
 
+      it "returns errors if cache:key is not a string" do
+        config = YAML.dump({ cache: { key: 1 }, rspec: { script: "test" } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:key parameter should be a string")
+      end
+
+      it "returns errors if job cache:key is not an a string" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { key: 1 } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:key parameter should be a string")
+      end
+
       it "returns errors if job cache:untracked is not an array of strings" do
         config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { untracked: "string" } } })
         expect do