これは、なにをしたくて書いたもの?
GitLab CI/CDのキャッシュについて、雰囲気しか見ていなかったのでちゃんとドキュメントを見ておこうかなと。
GitLab CI/CDのキャッシュ
ドキュメントとして見ておくものはこちらですね。
キャッシュとはなにか?ですが、GitLab CI/CDにおいてジョブがダウンロードして保存する1つ以上のファイルのことを指します。
同じキャッシュを使用する後続のジョブは、ファイルを再度ダウンロードする必要がないため実行速度が向上します。
A cache is one or more files a job downloads and saves. Subsequent jobs that use the same cache don’t have to download the files again, so they execute more quickly.
Caching in GitLab CI/CD | GitLab Docs
ここで、少しキーワードを振り返っておくと
- ジョブ … タスクを実行するコマンドを定義したもの
- ステージ … ジョブをグループ化したもの
これらを構成要素としたものがパイプライン、という感じですね。
話をキャッシュに戻します。
- キャッシュは
cacheキーワードを使用してジョブごとに定義する - 後続のパイプラインではキャッシュを利用できる
- 依存関係が同一であれば、同じパイプラインの後続のジョブはキャッシュを使用できる
- 異なるプロジェクト間でキャッシュを共有することはできない
- デフォルトでは、ProtectedなブランチとProtectedではないブランチではキャッシュを共有しない
- この設定は変更可能
Caching in GitLab CI/CD / Cache
このページではこの後、キャッシュのベストプラクティスや使用例に移っていくのですが、設定項目の説明をしないので
このまま読んでいっても???という感じになります。
1度こちらを読みましょう。
なお、ここではキャッシュの特徴として最大4つまでということが書かれています。
設定の意味をまずは押さえておきましょう。気にしておいた方がよさそうなものを挙げてみます。
- cache:paths
- キャッシュするファイルまたはディレクトリーを指定する
- 配列での複数指定やglobを使ったワイルドカードの指定が可能
- プロジェクト内(
$CI_PROJECT_DIR)からの相対パスである必要がある- An array of paths relative to the project directory (
$CI_PROJECT_DIR).
- An array of paths relative to the project directory (
- node_modulesなどが指定される
- CI/CD YAML syntax reference / Job keywords / cache / cache:paths
- cache:key
- キャッシュを識別するための名前
- 同じキャッシュキーを使用するジョブは、異なるパイプラインであっても同じキャッシュを使用する
- 指定しなかった場合のデフォルトのキーは
default - Predefined CI/CD variablesが利用可能
- CI/CD YAML syntax reference / Job keywords / cache / cache:key
- cache:key:files
- 定義されたパスまたはパターンのいずれかに一致するファイルが変更された時に、新しいキーを生成する
- キーはファイルパスから生成することになる
- cache:key:filesは一部のキャッシュを再利用し、キャッシュを再構築する頻度を減らすことで実行速度を向上させる
pom.xmlやpackage.jsonなどが指定される- CI/CD YAML syntax reference / Job keywords / cache / cache:key:files
- cache:key:prefix
- cache:key:filesから計算したSHAハッシュ値に結合するprefixを定義する
- cache:key:filesを使った場合、キャッシュキーがハッシュ値のみに依存するため、異なるブランチでキャッシュを分けたい場合などはこちらでキャッシュキーを制御する
- CI/CD YAML syntax reference / Job keywords / cache / cache:key:prefix
- cache:when
- ジョブのステータスにもとづいて、キャッシュをいつ保存するかを定義する
- ジョブが成功した場合のみ保存する(デフォルト)、ジョブが失敗した場合のみ保存する、常に保存する、の3つから選択
- CI/CD YAML syntax reference / Job keywords / cache / cache:when
- cache:policy
- キャッシュのアップロードとダウンロードの動作を定義する
- デフォルトではジョブの開始時にキャッシュをダウンロードし、終了時にアップロードする(pull-push)
- ダウンロードのみ(pull)、アップロードのみ(push)、とすることも可能
- pullは同じキャッシュを使用するジョブを多数並列実行する場合に有効、pushはキャッシュを構築するジョブに利用する
- CI/CD YAML syntax reference / Job keywords / cache / cache:policy
- cache:fallback_keys
- cache:keyで指定したキャッシュが見つからなかった場合に、復元を試みるキーを指定する
- CI/CD YAML syntax reference / Job keywords / cache / cache:fallback_keys
cache:untrackedとcache:unprotect以外、全部載せてしまったような気がします(笑)。
なんとなくちゃんとリファレンスを読まないとcache:paths、cache:key、cache:key:filesの違いがわかりにくいような
気もするのですが、こうやって見た後にこちらのページの使用例を見ると、だいぶ意味がわかるようになります。
Caching in GitLab CI/CD | GitLab Docs
ここからですね。
Caching in GitLab CI/CD / Common use cases for caches
たとえば、キャッシュのキーはあくまでキャッシュの名前なので、こう書くと同じブランチで同じキャッシュを見るように
なることがわかります。
cache: key: $CI_COMMIT_REF_SLUG
Caching in GitLab CI/CD / Common use cases for caches / Share caches between jobs in the same branch
つまり、名前を工夫すればどのブランチ・ジョブの間で共有するかを工夫できます。
その後は言語ごとの設定例が出てくるので参考にするとよいでしょうね。
キャッシュキーを特定のファイルから計算させることもできますが、この場合はキャッシュキーが指定したファイルの
ハッシュ値になります。
default: cache: # Cache modules using lock file key: files: - package-lock.json paths: - .npm/
これだけだとブランチ間で共有されるので、それが嫌な場合はcache:key:prefixを指定してキャッシュキーを制御します。
キャッシュする対象は、プロジェクト内($CI_PROJECT_DIR)からの相対パスのものであるということを意識しておく
必要があります。
An array of paths relative to the project directory (
$CI_PROJECT_DIR).
CI/CD YAML syntax reference / Job keywords / cache / cache:paths
あと、グローバルなキャッシュという表現が出てきますが
Caching in GitLab CI/CD / Inherit global configuration, but override specific settings per job
ここで使われているdefaultキーワードはcacheが設定されていないジョブにコピーされるものになるので、結局ジョブごとに
キャッシュの設定を書いていることになると押さえておけばよいと思います。
Each default keyword is copied to every job that doesn’t already have it defined. If the job already has a keyword defined, that default is not used.
CI/CD YAML syntax reference / Global keywords / default
つまり、GitLab CI/CDの「キャッシュがある場所」から指定のキーでルックアップしている感じですね。
キャッシュの保存場所は、デフォルトではRunnerのローカル上に保存され、場所はRunnerのExecutorがShell、Docker、
Docker Machineのいずれで動作しているかで変わるようです。
- Shell Executor …
/home/gitlab-runner/cache/<user>/<project>/<cache-key>/cache.zip - Docker Executor、Docker Machine …
/var/lib/docker/volumes/<volume-id>/_data/<user>/<project>/<cache-key>/cache.zip
Caching in GitLab CI/CD / Availability of the cache / Where the caches are stored
Docker Executorの場合はキャッシュはvolumeとして表現されるということですね。
キャッシュのクリア方法についてはこちら。
Caching in GitLab CI/CD / Clearing the cache
キーを変更する方法と、GitLabのUIから操作する方法がありますが、キャッシュを「削除する」という意味では後者ですね。
前者は参照するキャッシュキーを変更することで無効化する、ということでしょう。
では、ドキュメントを見るのはこれくらいにして動かしてみましょうか。
環境
今回の環境はこちら。
$ sudo gitlab-rake gitlab:env:info System information System: Ubuntu 24.04 Current User: git Using RVM: no Ruby Version: 3.2.5 Gem Version: 3.6.9 Bundler Version:2.6.5 Rake Version: 13.0.6 Redis Version: 7.2.9 Sidekiq Version:7.3.9 Go Version: unknown GitLab information Version: 18.2.1 Revision: baccadafcda Directory: /opt/gitlab/embedded/service/gitlab-rails DB Adapter: PostgreSQL DB Version: 16.8 URL: http://192.168.0.6 HTTP Clone URL: http://192.168.0.6/some-group/some-project.git SSH Clone URL: git@192.168.0.6:some-group/some-project.git Using LDAP: no Using Omniauth: yes Omniauth Providers: GitLab Shell Version: 14.43.0 Repository storages: - default: unix:/var/opt/gitlab/gitaly/gitaly.socket GitLab Shell path: /opt/gitlab/embedded/service/gitlab-shell Gitaly - default Address: unix:/var/opt/gitlab/gitaly/gitaly.socket - default Version: 18.2.1 - default Git Version: 2.50.1.gl1 $ gitlab-runner --version Version: 18.2.1 Git revision: cc489270 Git branch: 18-2-stable GO version: go1.24.4 X:cacheprog Built: 2025-07-28T12:43:39Z OS/Arch: linux/amd64
GitLabは192.168.0.6で動作しているものとします。
環境はTerraformで作成します。
$ terraform version Terraform v1.12.2 on linux_amd64
サンプルコード用のJavaまわり。
$ java --version openjdk 21.0.8 2025-07-15 OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu124.04.1) OpenJDK 64-Bit Server VM (build 21.0.8+9-Ubuntu-0ubuntu124.04.1, mixed mode, sharing) $ mvn --version Apache Maven 3.9.10 (5f519b97e944483d878815739f519b2eade0a91d) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.8, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "6.8.0-71-generic", arch: "amd64", family: "unix"
GitLabプロジェクトの作成とGitLab Runnerの登録
最初にGitLabプロジェクトとGitLab Runnerのトークンの作成を行います。
terraform.tf
terraform { required_version = "1.12.2" required_providers { gitlab = { source = "gitlabhq/gitlab" version = "18.2.0" } } }
main.tf
variable "root_access_token" { type = string ephemeral = true } provider "gitlab" { token = var.root_access_token base_url = "http://192.168.0.6/" } resource "gitlab_group" "sample_group" { name = "sample group" path = "sample-group" } resource "gitlab_project" "sample_app" { name = "sample-app" namespace_id = gitlab_group.sample_group.id default_branch = "main" visibility_level = "private" auto_devops_enabled = false only_allow_merge_if_pipeline_succeeds = true only_allow_merge_if_all_discussions_are_resolved = true } resource "gitlab_branch_protection" "main_branch" { project = gitlab_project.sample_app.id branch = "main" allow_force_push = false merge_access_level = "maintainer" push_access_level = "no one" unprotect_access_level = "maintainer" } resource "gitlab_group_membership" "sample_user" { group_id = gitlab_group.sample_group.id user_id = gitlab_user.sample_user.id access_level = "owner" } resource "gitlab_user" "sample_user" { name = "sample-user" username = "sample-user" password = "P@ssw0rd" email = "sample-user@example.com" } resource "gitlab_personal_access_token" "sample_user" { user_id = gitlab_user.sample_user.id name = "access token" expires_at = "2025-09-11" scopes = ["api"] } resource "gitlab_user_runner" "group_runner" { runner_type = "group_type" group_id = gitlab_group.sample_group.id description = "sample group runner" untagged = true } output "runner_authentication_token" { value = gitlab_user_runner.group_runner.token sensitive = true }
$ export TF_VAR_root_access_token=...
リソースを作成。
$ terraform init $ terraform apply
GitLab Runnerのトークンを確認して
$ terraform output runner_authentication_token "glrt-xxxxxxxxxx"
GitLab RunenrをGitLabに登録します。
$ RUNNER_TOKEN=... $ sudo gitlab-runner register \ --non-interactive \ --url "http://192.168.0.6/" \ --token "$RUNNER_TOKEN" \ --executor "docker" \ --docker-image ubuntu:24.04 \ --description "sample group runner"
設定ファイルはこちら。
/etc/gitlab-runner/config.toml
concurrent = 1
check_interval = 0
shutdown_timeout = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "sample group runner"
url = "http://192.168.0.6/"
id = 6
token = "..."
token_obtained_at = 2025-08-11T14:11:48Z
token_expires_at = 0001-01-01T00:00:00Z
executor = "docker"
[runners.cache]
MaxUploadedArchiveSize = 0
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
tls_verify = false
image = "ubuntu:24.04"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
network_mtu = 0
サンプルアプリケーションを作成して、キャッシュを使ったGitLab CI/CDパイプラインを動かす
それでは、サンプルアプリケーションを作成してGitLab CI/CDパイプラインを動かしてみましょう。
ローカルディレクトリーをGitリポジトリーとして初期化して、README.mdだけmainブランチに登録します。
$ git init --initial-branch=main $ git config --local user.name "sample-user" $ git config --local user.email "sample-user@example.com" $ git remote add origin http://192.168.0.6/sample-group/sample-app.git $ touch README.md $ git add README.md $ git commit -m "Initial commit" $ git push --set-upstream origin main
サンプルコードを作成。
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.littlewings</groupId> <artifactId>sample-app</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.13.4</version> <scope>test</scope> </dependency> </dependencies> </project>
src/main/java/org/littlewings/gitlab/CalcService.java
package org.littlewings.gitlab; public class CalcService { public int plus(int a, int b) { return a + b; } }
src/test/java/org/littlewings/gitlab/CalcServiceTest.java
package org.littlewings.gitlab; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class CalcServiceTest { @Test void plus() { CalcService sut = new CalcService(); Assertions.assertEquals(5, sut.plus(2, 3)); } }
GitLab CI/CDの設定も書いておきます。
.gitlab-ci.yml
stages: - build - test - deploy default: image: maven:3.9.10-eclipse-temurin-21 workflow: rules: # Merge Requestを対象 - if: $CI_PIPELINE_SOURCE == "merge_request_event" # デフォルトブランチを対象 - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Protectedブランチを対象 - if: $CI_COMMIT_REF_PROTECTED == "true" # 手動実行 - if: $CI_PIPELINE_SOURCE == "web" # それ以外は実行しない - when: never compile: stage: build script: - mvn compile test: stage: test script: - mvn test packaging: stage: deploy script: - mvn package -DskipTests
こちらを新しいブランチにして登録。
$ git switch -c add-code $ git add src pom.xml .gitlab-ci.yml $ git commit -m 'add, sources' $ git push origin HEAD
ワークフローを設定しているので、このブランチに対してMerge Requestを作成します。
workflow: rules: # Merge Requestを対象 - if: $CI_PIPELINE_SOURCE == "merge_request_event" # デフォルトブランチを対象 - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Protectedブランチを対象 - if: $CI_COMMIT_REF_PROTECTED == "true" # 手動実行 - if: $CI_PIPELINE_SOURCE == "web" # それ以外は実行しない - when: never
するとパイプラインが動き出しますが、Mavenプラグインや依存ライブラリーは毎回ダウンロードされます。
mvn compileの時。

mvn testの時。

では、GitLab CI/CDのパイプライン設定をこう変更します。
.gitlab-ci.yml
stages: - build - test - deploy variables: MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end" ## ダウンロード表示を出したくない場合は、--no-transfer-progressを入れる # MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end --no-transfer-progress" default: image: maven:3.9.10-eclipse-temurin-21 cache: key: files: - pom.xml paths: - .m2/repository workflow: rules: # Merge Requestを対象 - if: $CI_PIPELINE_SOURCE == "merge_request_event" # デフォルトブランチを対象 - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Protectedブランチを対象 - if: $CI_COMMIT_REF_PROTECTED == "true" # 手動実行 - if: $CI_PIPELINE_SOURCE == "web" # それ以外は実行しない - when: never compile: stage: build script: - mvn ${MAVEN_CLI_OPTS} compile test: stage: test script: - mvn ${MAVEN_CLI_OPTS} test packaging: stage: deploy script: - mvn ${MAVEN_CLI_OPTS} package -DskipTests
今回はすべてのジョブに同じ設定を適用しました。
default: image: maven:3.9.10-eclipse-temurin-21 cache: key: files: - pom.xml paths: - .m2/repository
pom.xmlの変更有無からキーを作成するようにしていますね。
キャッシュ対象は.m2/repositoryで、これをMavenのローカルリポジトリーとするようにしています。
variables: MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end" ## ダウンロード表示を出したくない場合は、--no-transfer-progressを入れる # MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end --no-transfer-progress" ... compile: stage: build script: - mvn ${MAVEN_CLI_OPTS} compile test: stage: test script: - mvn ${MAVEN_CLI_OPTS} test packaging: stage: deploy script: - mvn ${MAVEN_CLI_OPTS} package -DskipTests
こうするのは、キャッシュ対象にできるのはプロジェクト内のディレクトリーだけだからですね。
ちなみに、Apache Mavenに--no-transfer-progressというオプションを指定すると、ダウンロードログを表示しなくなります。
MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end" ## ダウンロード表示を出したくない場合は、--no-transfer-progressを入れる # MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end --no-transfer-progress"
更新したファイルをコミットしてpush。
$ git add .gitlab-ci.yml $ git commit -m 'add, cache' $ git push origin HEAD
最初のジョブであるmvn compile時は変化がありませんが、

その後のmvn test時はその続きからの依存ライブラリー等のダウンロードになります。

2回目のパイプライン実行では、完全にダウンロードされなくなります。

この後、pom.xmlを変更すると新しくキャッシュが生成されることが確認できました。また、pom.xmlが変わらないと
ブランチが変わってもキャッシュが再利用されますね。これが嫌な場合はcache:key:prefixを使うのでしょう。
ひとまず、どういうものか感覚はわかったかなと思います。
おわりに
GitLab CI/CDのキャッシュの設定方法を確認してみました。
ドキュメントを斜め読みしているだけだと、設定で出てくるキーワードがよくわからなかったりしたのですが、.gitlab-ci.ymlの
リファレンスを落ち着いて読むとだいぶ理解が進みましたね。
これで扱えるようにはなったかなと思います。