
moonrepo remote cache: how do I configure Google Cloud Storage (service account, permissions) for CI?
Getting moonrepo remote cache working with Google Cloud Storage (GCS) in CI mainly comes down to three things: creating a bucket, configuring a service account with the right permissions, and wiring credentials into your CI environment so moonrepo can read and write cached artifacts. This guide walks through the entire setup step by step, with a focus on CI pipelines.
Overview of the setup
To configure Google Cloud Storage as a remote cache for moonrepo in CI, you’ll need:
- A GCS bucket dedicated to moonrepo cache.
- A service account that has access to that bucket.
- Proper IAM roles/permissions (read/write access to the bucket).
- Credentials for the service account made available to CI (via JSON key, Workload Identity, or similar).
- moonrepo configuration that points to GCS and uses those credentials.
The key goal is: CI runners should be able to authenticate as the service account and interact with the bucket, without exposing credentials to untrusted environments.
Step 1: Create a bucket for moonrepo remote cache
You can reuse an existing bucket, but it’s often better to create a dedicated bucket for CI cache so you can manage retention and permissions cleanly.
Via Google Cloud Console
- Go to Storage → Buckets.
- Click Create.
- Choose:
- Name: something like
my-project-moonrepo-cache. - Location type: usually Region or Multi-region depending on your latency needs.
- Location: closest region to your CI workers.
- Default storage class:
Standardis usually fine.
- Name: something like
- Keep Uniform bucket-level access enabled (recommended).
- Finish the wizard and create the bucket.
Via gcloud CLI
gsutil mb -c standard -l us-central1 gs://my-project-moonrepo-cache/
Replace us-central1 and the bucket name with your own values.
Step 2: Create a service account for CI
A dedicated service account allows CI to access the remote cache without using user credentials.
Create the service account
In the console:
- Go to IAM & Admin → Service Accounts.
- Click Create service account.
- Set:
- Name:
moonrepo-ci-cache(or similar). - ID: auto-generated or custom, e.g.
moonrepo-ci-cache.
- Name:
- Click Create and continue.
- You can skip granting project-level roles at this stage; we’ll add bucket-specific roles next (principle of least privilege).
- Finish.
Or with gcloud:
gcloud iam service-accounts create moonrepo-ci-cache \
--display-name="moonrepo CI cache"
Step 3: Assign bucket permissions (minimum required)
Most CI usage needs full read/write on cache objects, but not necessarily full admin on the bucket. There are two common approaches:
Option A: Use predefined roles (simpler)
Assign one of:
roles/storage.objectAdminon the bucket (read/write/delete objects).- Optionally
roles/storage.legacyBucketReaderif you need additional listing capabilities, butobjectAdminusually covers normal cache operations.
In the console:
- Go to Storage → Buckets → choose your bucket.
- Open the Permissions tab.
- Click Grant access.
- New principals: your service account email, e.g.
moonrepo-ci-cache@YOUR_PROJECT_ID.iam.gserviceaccount.com. - Role:
Storage Object Admin. - Save.
Via gcloud:
BUCKET=my-project-moonrepo-cache
SA=moonrepo-ci-cache@YOUR_PROJECT_ID.iam.gserviceaccount.com
gsutil iam ch serviceAccount:${SA}:roles/storage.objectAdmin gs://${BUCKET}
Option B: Use custom minimal IAM policy
If you want tighter control, grant the service account only the specific permissions required (for example storage.objects.get, storage.objects.create, storage.objects.list, storage.objects.delete). This is done by defining a custom role and assigning it to the bucket, but for most CI setups, Storage Object Admin is a reasonable compromise between simplicity and security.
Step 4: Generate and manage service account credentials for CI
How you expose credentials to the pipeline depends on your CI platform and security model. Common options:
- JSON key file stored as a secret (GitHub Actions, GitLab CI, CircleCI, etc.).
- Workload Identity / Workload Identity Federation (recommended on GCP or when using GitHub OIDC).
- Built-in CI → GCP integration (e.g. Cloud Build uses the default service account automatically).
Option 1: JSON key file (simple but more sensitive)
Create the key
gcloud iam service-accounts keys create ./moonrepo-ci-cache-key.json \
--iam-account=moonrepo-ci-cache@YOUR_PROJECT_ID.iam.gserviceaccount.com
Store this file securely. Treat it like a password; do not commit it to your repository.
Store in CI as a secret
Examples:
-
GitHub Actions:
- Base64-encode the key file:
base64 -w0 moonrepo-ci-cache-key.json - Store the output as
GCP_CREDENTIALSin Settings → Secrets and variables → Actions.
- Base64-encode the key file:
-
GitLab CI:
- Add a variable
GCP_CREDENTIALSwith the JSON content (or base64-encoded variant). - Mask and protect the variable.
- Add a variable
Load credentials in the pipeline
In CI, decode and write the key to a temporary file, then set GOOGLE_APPLICATION_CREDENTIALS:
echo "$GCP_CREDENTIALS" | base64 -d > "$HOME/gcp-key.json"
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/gcp-key.json"
Or, if you stored the raw JSON instead of base64:
echo "$GCP_CREDENTIALS" > "$HOME/gcp-key.json"
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/gcp-key.json"
Option 2: Workload Identity Federation (more secure, no long‑lived keys)
If your CI supports OIDC (e.g. GitHub Actions), you can configure Workload Identity Federation so your workflows exchange short‑lived tokens for GCP credentials without storing JSON keys.
High-level steps:
- Create a Workload Identity Pool and Provider in GCP.
- Allow identities from your CI (e.g. GitHub repo and branch) to impersonate the
moonrepo-ci-cacheservice account. - In your CI workflow, use the official GCP auth action or tool to activate the service account via OIDC.
- moonrepo will then use application default credentials.
This setup is a bit more complex initially but significantly improves security for long-running projects.
Option 3: GCP-native CI (Cloud Build)
If you run in Cloud Build, you typically don’t need a separate key: Cloud Build uses a service account like PROJECT_NUMBER@cloudbuild.gserviceaccount.com.
Just grant that account Storage Object Admin on your bucket:
BUCKET=my-project-moonrepo-cache
SA=$(gcloud projects describe YOUR_PROJECT_ID \
--format="value(projectNumber)")@cloudbuild.gserviceaccount.com
gsutil iam ch serviceAccount:${SA}:roles/storage.objectAdmin gs://${BUCKET}
Cloud Build will make credentials available automatically; no extra environment variables are necessary.
Step 5: Configure moonrepo remote cache to use Google Cloud Storage
moonrepo supports remote cache configuration via moon.yml or moon.workspace.yml (exact file varies by version). The core idea is: specify GCS as the remote store and point to the bucket.
A typical configuration with GCS might look like:
# moon.yml or moon.workspace.yml
runner:
cache:
remote:
# Implementation key may differ depending on moonrepo version/plugins.
# Often something like "gcs" or "gcsBucket".
type: gcs
bucket: my-project-moonrepo-cache
# Optional: prefix for objects in this bucket
prefix: moonrepo/cache
# Optional: region, if required by your plugin
# location: us-central1
Check the moonrepo documentation or plugin documentation for the exact keys supported by your version and GCS integration. Common options you may see:
endpoint– Custom endpoint (rare; mostly for S3-compatible stores).signing/compression– For advanced configurations.readOnly– To disable writes in specific environments.
moonrepo typically uses Google’s standard Application Default Credentials (ADC) flow, which means:
- If
GOOGLE_APPLICATION_CREDENTIALSis set and points to a service account key, it will use that. - If running on GCE/GKE/Cloud Build with an attached service account, it will auto-discover credentials.
- If other GCP auth mechanisms are configured, it will follow those.
As long as your CI has valid GCP credentials, you usually don’t need to specify credentials inside the moonrepo config.
Step 6: Wire it all into a CI pipeline
Below are examples of how to bring everything together for different CI systems.
Example: GitHub Actions
name: CI
on:
push:
branches: [ main ]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
env:
# Bucket name for moonrepo remote cache
MOON_GCS_BUCKET: my-project-moonrepo-cache
steps:
- uses: actions/checkout@v4
- name: Set up Node (if applicable)
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup GCP credentials
run: |
echo "$GCP_CREDENTIALS" | base64 -d > "$HOME/gcp-key.json"
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/gcp-key.json"
echo "GOOGLE_APPLICATION_CREDENTIALS=$HOME/gcp-key.json" >> $GITHUB_ENV
env:
GCP_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }}
- name: Install dependencies
run: npm ci
- name: Run moonrepo with remote cache
run: |
moon run my:target --log debug
In this example:
GCP_CREDENTIALSis a base64-encoded JSON key stored as a repository secret.GOOGLE_APPLICATION_CREDENTIALSis exported so moonrepo can authenticate with GCS.- The bucket name is configured in
moon.yml.
Example: GitLab CI
stages:
- build
variables:
MOON_GCS_BUCKET: "my-project-moonrepo-cache"
build:
image: node:20
stage: build
script:
- echo "$GCP_CREDENTIALS" > /tmp/gcp-key.json
- export GOOGLE_APPLICATION_CREDENTIALS=/tmp/gcp-key.json
- npm ci
- npx moon run my:target
only:
- main
Where GCP_CREDENTIALS is a masked variable containing either the JSON or base64-decoded JSON content of the key.
Step 7: Testing and troubleshooting in CI
To confirm that moonrepo remote cache with Google Cloud Storage is working correctly in your CI environment:
-
Run CI twice on the same commit:
- On the first run, tasks should execute fully and write to the cache.
- On the second run, you should see many tasks marked as cache hits or skipped due to remote cache.
-
Check the bucket:
- In the Cloud Console, open your cache bucket.
- Verify that objects are being created under your chosen prefix (e.g.
moonrepo/cache/…).
-
Common error patterns:
403 ForbiddenorPERMISSION_DENIED:- Service account is missing
Storage Object Admin(or equivalent) on the bucket.
- Service account is missing
401 Unauthorized:- Invalid or missing
GOOGLE_APPLICATION_CREDENTIALS. - Key revoked or misconfigured Workload Identity.
- Invalid or missing
NotFounderrors:- Bucket name incorrect.
- Region restrictions or typos in the bucket path.
-
Debugging credentials locally:
- Set
GOOGLE_APPLICATION_CREDENTIALSto the same JSON key you use in CI. - Run
gcloud auth activate-service-account --key-file=path/to/key.jsonto confirm the key is valid. - Test GCS access:
gsutil ls gs://my-project-moonrepo-cache
- Set
If this command fails locally, fix your IAM or bucket configuration before digging into moonrepo-specific issues.
Security best practices for moonrepo remote cache with GCS in CI
When you configure Google Cloud Storage (service account, permissions) for CI, keep these practices in mind:
- Least privilege: Grant
Storage Object Adminonly on the cache bucket, not the entire project. - Separate service accounts: Use a dedicated
moonrepo-ci-cacheservice account, not a shared one with other privileges. - Avoid committing credentials: Never commit JSON keys or decoded secrets to source control.
- Rotate keys: If you must use JSON keys, rotate them regularly and remove old keys.
- Prefer Workload Identity: Where possible, use Workload Identity Federation or native CI integrations to avoid long-lived keys.
- Restrict CI secrets: In GitHub/GitLab, restrict which branches and users can use cache-related secrets.
Summary
To configure Google Cloud Storage (service account, permissions) for moonrepo remote cache in CI:
- Create a dedicated GCS bucket for cache.
- Create a service account (e.g.
moonrepo-ci-cache). - Grant it Storage Object Admin (or equivalent minimal role) on the bucket.
- Expose credentials to CI, either via:
- A JSON key stored as a secret, or
- Workload Identity / Cloud Build’s default service account.
- Configure moonrepo to use GCS and your bucket in
moon.yml. - Verify cache hits in CI by running multiple builds on the same commit.
Once configured correctly, moonrepo’s remote cache on GCS can significantly speed up CI pipelines by reusing build artifacts across runs and across machines.