bug: actions/cache has inconsistent behavior in LXC and Docker containers. #918

Closed
opened 2025-08-28 17:33:47 +00:00 by Hill-98 · 17 comments

Can you reproduce the bug on the Forgejo test instance?

No

Description

This is an extension of issue #916

I ran the following workflow twice and the LXC container always runs with Cache not found for input keys: lxc-cache, but if I remove the ${{ forge.workspace }}/ from the path parameter, it works fine.

either way, after the workflow execution is complete, I see the message in the output that the cache was saved.

my runner config:

{
  "WARNING": "This file is automatically generated by act-runner. Do not edit it manually unless you know what you are doing. Removing this file will cause act runner to re-register as a new runner.",
  "id": 3,
  "uuid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "name": "forgejo-runner",
  "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "address": "https://git.xxx.fun/",
  "labels": [
    "docker:docker://catthehacker/ubuntu:act-latest",
    "ubuntu-latest:docker://catthehacker/ubuntu:act-latest",
    "lxc:lxc://debian:bookworm"
  ]
}

Forgejo Version

12.0.1+gitea-1.22.0

Runner Version

9.1.1

How are you running Forgejo?

Docker

How are you running the Runner?

Debian Bookworm on QEMU VM

Logs

No response

Workflow file

name: actions/cache test

on:
  workflow_dispatch:

jobs:
  docker-cache-test:
    runs-on: docker
    steps:
      - name: cache test
        id: cache
        uses: actions/cache@v4
        with:
          path: ${{ forge.workspace }}/cache
          key: docker-cache

      - name: write cache
        if: ${{ steps.cache.outputs.cache-hit != 'true' }}
        run: date > ${{ forge.workspace }}/cache

      - name: read cacha
        run: cat ${{ forge.workspace }}/cache
  lxc-cache-test:
    runs-on: lxc
    steps:
      - name: cache test
        id: cache
        uses: actions/cache@v4
        with:
          path: ${{ forge.workspace }}/cache
          key: lxc-cache

      - name: write cache
        if: ${{ steps.cache.outputs.cache-hit != 'true' }}
        run: date > ${{ forge.workspace }}/cache

      - name: read cacha
        run: cat ${{ forge.workspace }}/cache
### Can you reproduce the bug on the Forgejo test instance? No ### Description This is an extension of issue #916 I ran the following workflow twice and the LXC container always runs with `Cache not found for input keys: lxc-cache`, but if I remove the `${{ forge.workspace }}/` from the `path` parameter, it works fine. either way, after the workflow execution is complete, I see the message in the output that the cache was saved. my runner config: ```json { "WARNING": "This file is automatically generated by act-runner. Do not edit it manually unless you know what you are doing. Removing this file will cause act runner to re-register as a new runner.", "id": 3, "uuid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "name": "forgejo-runner", "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "address": "https://git.xxx.fun/", "labels": [ "docker:docker://catthehacker/ubuntu:act-latest", "ubuntu-latest:docker://catthehacker/ubuntu:act-latest", "lxc:lxc://debian:bookworm" ] } ``` ### Forgejo Version 12.0.1+gitea-1.22.0 ### Runner Version 9.1.1 ### How are you running Forgejo? Docker ### How are you running the Runner? Debian Bookworm on QEMU VM ### Logs _No response_ ### Workflow file ```yaml name: actions/cache test on: workflow_dispatch: jobs: docker-cache-test: runs-on: docker steps: - name: cache test id: cache uses: actions/cache@v4 with: path: ${{ forge.workspace }}/cache key: docker-cache - name: write cache if: ${{ steps.cache.outputs.cache-hit != 'true' }} run: date > ${{ forge.workspace }}/cache - name: read cacha run: cat ${{ forge.workspace }}/cache lxc-cache-test: runs-on: lxc steps: - name: cache test id: cache uses: actions/cache@v4 with: path: ${{ forge.workspace }}/cache key: lxc-cache - name: write cache if: ${{ steps.cache.outputs.cache-hit != 'true' }} run: date > ${{ forge.workspace }}/cache - name: read cacha run: cat ${{ forge.workspace }}/cache ```
Contributor

Could you please attach the logs?

Could you please attach the logs?
Contributor

This is a good reproducer, I'll try that today.

This is a good reproducer, I'll try that today.
Contributor

I copy/pasted the workflow verbatim. With a runner 9.1.1 installed from binary with the following config, it works for me 🤔

log:
  level: trace
  job_level: trace
runner:
  file: .runner
  capacity: 1
  envs:
    A_TEST_ENV_NAME_1: a_test_env_value_1
    A_TEST_ENV_NAME_2: a_test_env_value_2
  env_file: .env
  timeout: 3h
  shutdown_timeout: 3h
  insecure: false
  fetch_timeout: 5s
  fetch_interval: 2s
  report_interval: 1s
  labels: ["ubuntu-latest:docker://node:20-bookworm","docker:docker://node:20-bookworm", "lxc:lxc://debian:bookworm"]
cache:
  enabled: true
  dir: ""
  host: ""
  port: 0
  proxy_port: 0
  external_server: ""
  secret: ""
  actions_cache_url_override: ""
container:
  network: ""
  enable_ipv6: false
  privileged: false
  options:
  workdir_parent:
  valid_volumes: []
  docker_host: "-"
  force_pull: false
  force_rebuild: false
host:
  workdir_parent:

https://v13.next.forgejo.org/earl-warren/lxc-cache/actions/runs/1/jobs/1

image

I copy/pasted the workflow verbatim. With a runner 9.1.1 installed from binary with the following config, it works for me 🤔 ```yaml log: level: trace job_level: trace runner: file: .runner capacity: 1 envs: A_TEST_ENV_NAME_1: a_test_env_value_1 A_TEST_ENV_NAME_2: a_test_env_value_2 env_file: .env timeout: 3h shutdown_timeout: 3h insecure: false fetch_timeout: 5s fetch_interval: 2s report_interval: 1s labels: ["ubuntu-latest:docker://node:20-bookworm","docker:docker://node:20-bookworm", "lxc:lxc://debian:bookworm"] cache: enabled: true dir: "" host: "" port: 0 proxy_port: 0 external_server: "" secret: "" actions_cache_url_override: "" container: network: "" enable_ipv6: false privileged: false options: workdir_parent: valid_volumes: [] docker_host: "-" force_pull: false force_rebuild: false host: workdir_parent: ``` https://v13.next.forgejo.org/earl-warren/lxc-cache/actions/runs/1/jobs/1 ![image](/attachments/9b15b9e3-c326-4fab-9717-4cecf8b17884)
Author

The second time the workflow is running, LXC still has no cache found.

Is the same result of your test?

The second time the workflow is running, LXC still has no cache found. Is the same result of your test?
Author

My previous wording may not have been very clear. If the workflow is run twice, the Docker container can restore the cache on the second run, but the LXC container always outputs "cache not found."

My previous wording may not have been very clear. If the workflow is run twice, the Docker container can restore the cache on the second run, but the LXC container always outputs "cache not found."
Contributor
I get it now. ## First run https://v13.next.forgejo.org/earl-warren/lxc-cache/actions/runs/1/jobs/1 Both Docker & LXC populate the cache ![image](/attachments/9b15b9e3-c326-4fab-9717-4cecf8b17884) ## Second run [Docker retrieves the cache successfully](https://v13.next.forgejo.org/earl-warren/lxc-cache/actions/runs/2/jobs/0) ![image](/attachments/05eb33c0-00ef-416a-a39f-1c7836aba1ca) [LXC does not retrieve the cache successfully](https://v13.next.forgejo.org/earl-warren/lxc-cache/actions/runs/2/jobs/1) ![image](/attachments/e0740a13-206d-4797-8616-381aeecb4948)
Contributor

With this workflow (i.e. path: cache instead of path: ${{ forge.workspace }}/cache)

name: actions/cache test

on:
  workflow_dispatch:

jobs:
  lxc-cache-test:
    runs-on: lxc
    steps:
      - name: cache test
        id: cache
        uses: actions/cache@v4
        with:
          path: cache
          key: lxc-cache

      - name: write cache
        if: ${{ steps.cache.outputs.cache-hit != 'true' }}
        run: date > ${{ forge.workspace }}/cache

      - name: read cacha
        run: cat ${{ forge.workspace }}/cache

First run

Populates the cache

https://v13.next.forgejo.org/earl-warren/lxc-cache/actions/runs/3

image

Second run

Restores the cache

https://v13.next.forgejo.org/earl-warren/lxc-cache/actions/runs/4

image

With this workflow (i.e. `path: cache` instead of `path: ${{ forge.workspace }}/cache`) ```yaml name: actions/cache test on: workflow_dispatch: jobs: lxc-cache-test: runs-on: lxc steps: - name: cache test id: cache uses: actions/cache@v4 with: path: cache key: lxc-cache - name: write cache if: ${{ steps.cache.outputs.cache-hit != 'true' }} run: date > ${{ forge.workspace }}/cache - name: read cacha run: cat ${{ forge.workspace }}/cache ``` ## First run Populates the cache https://v13.next.forgejo.org/earl-warren/lxc-cache/actions/runs/3 ![image](/attachments/ca580772-bc87-4a25-8472-a297a0f88deb) ## Second run Restores the cache https://v13.next.forgejo.org/earl-warren/lxc-cache/actions/runs/4 ![image](/attachments/21543b0e-1141-49ea-b575-30fae602fba4)
Contributor

@Hill-98 it is perfectly reproducible now 👍

@Hill-98 it is perfectly reproducible now 👍
Contributor

There is no guarantee that ${{ forge.workspace }} is a constant. It turns out to be for Docker/podman based jobs (e.g. /workspace/earl-warren/lxc-cache). But for LXC based jobs it contains a random path component (e.g. /home/earl-warren/.cache/act/9dc369b9a28961d6/hostexecutor where 9dc369b9a28961d6 is the random part).

This is why the second run cannot retrieve the cache from the previous run: they both have different paths. And also the reason why it succeeds when using cache alone, because it is the same from one run to the next.

Does that clarify the issue?

There is no guarantee that `${{ forge.workspace }}` is a constant. It turns out to be for Docker/podman based jobs (e.g. `/workspace/earl-warren/lxc-cache`). But for LXC based jobs it contains a random path component (e.g. `/home/earl-warren/.cache/act/9dc369b9a28961d6/hostexecutor` where `9dc369b9a28961d6` is the random part). This is why the second run cannot retrieve the cache from the previous run: they both have different paths. And also the reason why it succeeds when using `cache` alone, because it is the same from one run to the next. Does that clarify the issue?
Author

I see. So would changing actions/cache or the cache server fix this?

I see. So would changing `actions/cache` or the cache server fix this?
Contributor

I acknowledge this is confusing (I was confused myself... ) because it is not immediately intuitive that ${{ forge.workspace }}/mydirectory may be evaluated to a different string on each job run. It should be documented.

But it does not seem like a bug to fix. You will get a consistent behavior if path: something/I/want/to/cache is identical across job runs.

Do you see what I mean?

I acknowledge this is confusing (I was confused myself... ) because it is not immediately intuitive that `${{ forge.workspace }}/mydirectory` may be evaluated to a different string on each job run. It should be documented. But it does not seem like a bug to fix. You will get a consistent behavior if `path: something/I/want/to/cache` is identical across job runs. Do you see what I mean?
Author

I see.

Do you think we should record this in the documentation?

I see. Do you think we should record this in the documentation?
Contributor

I thought about that but... actions/cache is where it would belong to be discoverable. Or even as a bug report there: the root of my confusion (and yours presumably) is that I don't understand why the path would matter to retrieve the entry when the key is unambiguous. But it does, evidently.

I thought about that but... `actions/cache` is where it would belong to be discoverable. Or even as a bug report there: the root of my confusion (and yours presumably) is that I don't understand why the `path` would matter to retrieve the entry when the `key` is unambiguous. But it does, evidently.
Author

Look at: https://github.com/actions/cache#cache-version

The version of the cache used internally is related to the path, so this is expected for GitHub, but broken for Forgejo's LXC.

Look at: https://github.com/actions/cache#cache-version The version of the cache used internally is related to the path, so this is expected for GitHub, but broken for Forgejo's LXC.
Contributor

The version of the cache used internally is related to the path,...

Good find, I missed it!

, so this is expected for GitHub, but broken for Forgejo's LXC.

It works as expected if the path is not random. ${{ forge.workspace }} is not guaranteed to be a fixed string across all jobs. It happens to be for Docker based runs in Forgejo Actions. But it is not for LXC based runs.

What is the problem with using something instead of ${{ forge.workspace }}/something for the path?

> The version of the cache used internally is related to the path,... Good find, I missed it! > , so this is expected for GitHub, but broken for Forgejo's LXC. It works as expected if the path is not random. `${{ forge.workspace }}` is not guaranteed to be a fixed string across all jobs. It happens to be for Docker based runs in Forgejo Actions. But it is not for LXC based runs. What is the problem with using `something` instead of `${{ forge.workspace }}/something` for the path?
Author

What is the problem with using something instead of ${{ forge.workspace }}/something for the path?

No problem, but the absolute path is always reassuring, lol...

In my current use case, this problem no longer affects me.

> What is the problem with using something instead of ${{ forge.workspace }}/something for the path? No problem, but the absolute path is always reassuring, lol... In my current use case, this problem no longer affects me.
Contributor

I agree absolute paths are reassuring. /opt/cache would also do.

Let's close this then.

I agree absolute paths are reassuring. `/opt/cache` would also do. Let's close this then.
Sign in to join this conversation.
No milestone
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
forgejo/runner#918
No description provided.