diff --git a/README.md b/README.md
index 586af06..1889561 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@ If you are using this inside a container, a POSIX-compliant `tar` needs to be in
 * `key` - An explicit key for restoring and saving the cache
 * `restore-keys` - An ordered list of keys to use for restoring stale cache if no cache hit occurred for key. Note
 `cache-hit` returns false in this case.
+* `always-save` - Flag indicating that it the cache should always be written
 
 ### Outputs
 
diff --git a/__tests__/restore.test.ts b/__tests__/restore.test.ts
index b4dbba9..53f67e0 100644
--- a/__tests__/restore.test.ts
+++ b/__tests__/restore.test.ts
@@ -293,6 +293,41 @@ test("restore with restore keys and no cache found", async () => {
     );
 });
 
+test("restore with always-save", async () => {
+    const path = "node_modules";
+    const key = "node-test";
+    testUtils.setInputs({
+        path: path,
+        key,
+        alwaysSave: true
+    });
+
+    const infoMock = jest.spyOn(core, "info");
+    const failedMock = jest.spyOn(core, "setFailed");
+    const stateMock = jest.spyOn(core, "saveState");
+    const restoreCacheMock = jest
+        .spyOn(cache, "restoreCache")
+        .mockImplementationOnce(() => {
+            return Promise.resolve(undefined);
+        });
+
+    process.env['GITHUB_RUN_ID'] = '1234'
+
+    await run();
+
+    process.env['GITHUB_RUN_ID'] = ''
+
+    expect(restoreCacheMock).toHaveBeenCalledTimes(1);
+    expect(restoreCacheMock).toHaveBeenCalledWith([path], `${key}-1234`, [`${key}-`]);
+
+    expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", `${key}-1234`);
+    expect(failedMock).toHaveBeenCalledTimes(0);
+
+    expect(infoMock).toHaveBeenCalledWith(
+        `Cache not found for input keys: ${key}-1234, ${key}-`
+    );
+});
+
 test("restore with cache found for key", async () => {
     const path = "node_modules";
     const key = "node-test";
diff --git a/__tests__/save.test.ts b/__tests__/save.test.ts
index 7598e0c..948abac 100644
--- a/__tests__/save.test.ts
+++ b/__tests__/save.test.ts
@@ -15,6 +15,12 @@ beforeAll(() => {
         return jest.requireActual("@actions/core").getInput(name, options);
     });
 
+    jest.spyOn(core, "getBooleanInput").mockImplementation((name, options) => {
+        return jest
+            .requireActual("@actions/core")
+            .getBooleanInput(name, options);
+    });
+
     jest.spyOn(actionUtils, "getCacheState").mockImplementation(() => {
         return jest.requireActual("../src/utils/actionUtils").getCacheState();
     });
diff --git a/action.yml b/action.yml
index 3e158e3..ddc7fd9 100644
--- a/action.yml
+++ b/action.yml
@@ -14,6 +14,9 @@ inputs:
   upload-chunk-size:
     description: 'The chunk size used to split up large files during upload, in bytes'
     required: false
+  always-save:
+    description: 'Flag indicating that it the cache should always be written'
+    required: false
 outputs:
   cache-hit:
     description: 'A boolean value to indicate an exact match was found for the primary key'
diff --git a/src/constants.ts b/src/constants.ts
index 133f47d..dd04377 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -2,7 +2,8 @@ export enum Inputs {
     Key = "key",
     Path = "path",
     RestoreKeys = "restore-keys",
-    UploadChunkSize = "upload-chunk-size"
+    UploadChunkSize = "upload-chunk-size",
+    AlwaysSave = "always-save"
 }
 
 export enum Outputs {
diff --git a/src/restore.ts b/src/restore.ts
index 648e4cb..35241c5 100644
--- a/src/restore.ts
+++ b/src/restore.ts
@@ -21,10 +21,20 @@ async function run(): Promise<void> {
             return;
         }
 
-        const primaryKey = core.getInput(Inputs.Key, { required: true });
-        core.saveState(State.CachePrimaryKey, primaryKey);
-
+        let primaryKey = core.getInput(Inputs.Key, { required: true });
         const restoreKeys = utils.getInputAsArray(Inputs.RestoreKeys);
+
+        // https://github.com/actions/toolkit/issues/844
+        let alwaysSave = false;
+        if (core.getInput(Inputs.AlwaysSave)) {
+            alwaysSave = core.getBooleanInput(Inputs.AlwaysSave);
+        }
+        if (alwaysSave) {
+            restoreKeys.push(`${primaryKey}-`);
+            primaryKey = `${primaryKey}-${process.env['GITHUB_RUN_ID'] ?? Date.now()}`;
+        }
+
+        core.saveState(State.CachePrimaryKey, primaryKey);
         const cachePaths = utils.getInputAsArray(Inputs.Path, {
             required: true
         });
diff --git a/src/save.ts b/src/save.ts
index 7b333fb..d8ad484 100644
--- a/src/save.ts
+++ b/src/save.ts
@@ -27,7 +27,7 @@ async function run(): Promise<void> {
         const state = utils.getCacheState();
 
         // Inputs are re-evaluted before the post action, so we want the original key used for restore
-        const primaryKey = core.getState(State.CachePrimaryKey);
+        let primaryKey = core.getState(State.CachePrimaryKey);
         if (!primaryKey) {
             utils.logWarning(`Error retrieving key from state.`);
             return;
diff --git a/src/utils/testUtils.ts b/src/utils/testUtils.ts
index 9e2134f..ae44bbb 100644
--- a/src/utils/testUtils.ts
+++ b/src/utils/testUtils.ts
@@ -13,6 +13,7 @@ interface CacheInput {
     path: string;
     key: string;
     restoreKeys?: string[];
+    alwaysSave?: boolean;
 }
 
 export function setInputs(input: CacheInput): void {
@@ -20,6 +21,7 @@ export function setInputs(input: CacheInput): void {
     setInput(Inputs.Key, input.key);
     input.restoreKeys &&
         setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
+    setInput(Inputs.AlwaysSave, input.alwaysSave ? "true" : "");
 }
 
 export function clearInputs(): void {