> ## Documentation Index
> Fetch the complete documentation index at: https://docs.archil.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Google Cloud Run

> Mount an Archil disk in a Cloud Run service so its files outlive every instance and revision.

[Cloud Run](https://cloud.google.com/run/docs/overview/what-is-cloud-run) runs your container on instances that come and go as traffic changes, and anything an instance writes to its own filesystem is gone when it shuts down. Mount an Archil disk in the container and those files stay on the disk, where every other instance, the next revision, and the Archil SDK can read them.

Cloud Run's second-generation execution environment supports FUSE, so the disk mounts as a regular directory with no extra permissions. Mount it when the container starts and unmount it when Cloud Run sends `SIGTERM`.

## Prerequisites

* The [gcloud CLI](https://cloud.google.com/sdk/docs/install), signed in to a project with billing enabled.
* An Archil disk, ideally in the Archil region closest to your Cloud Run region, such as `gcp-us-central1` for `us-central1`. See [Region availability](https://docs.archil.com/reference/regions.md).

Set your Archil API key and region:

```bash theme={null}
export ARCHIL_API_KEY=key-...
export ARCHIL_REGION=gcp-us-central1
```

Create the disk with the [`disk` CLI](https://docs.archil.com/reference/disk-cli). The output includes the disk ID and a one-time [mount token](https://docs.archil.com/concepts/authorization.md#token-based-disk-authorization), which grants access to this disk only:

```bash theme={null}
npx disk create cloud-run-data
```

Export both for the steps below, along with your Google Cloud project and region:

```bash theme={null}
export ARCHIL_DISK_ID=dsk-...
export ARCHIL_MOUNT_TOKEN=adt_...
export PROJECT=$(gcloud config get-value project)
export REGION=us-central1
```

## Set up the project

Enable the APIs the steps below use. Cloud Run builds and runs the service as the Compute Engine default service account unless you choose another, so give that account permission to build the image, and store the mount token in Secret Manager where only that account can read it, rather than in the service's environment variables:

```bash theme={null}
gcloud services enable run.googleapis.com cloudbuild.googleapis.com secretmanager.googleapis.com
SERVICE_ACCOUNT=$(gcloud projects describe "$PROJECT" --format='value(projectNumber)')-compute@developer.gserviceaccount.com
gcloud projects add-iam-policy-binding "$PROJECT" \
  --member "serviceAccount:$SERVICE_ACCOUNT" --role roles/run.builder --condition None
printf %s "$ARCHIL_MOUNT_TOKEN" | gcloud secrets create archil-mount-token --data-file=-
gcloud secrets add-iam-policy-binding archil-mount-token \
  --member "serviceAccount:$SERVICE_ACCOUNT" --role roles/secretmanager.secretAccessor
```

## Build the service

The image needs the `archil` client and your app:

```dockerfile Dockerfile theme={null}
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates \
 && curl -fsSL https://archil.com/install | sh \
 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY server.py .
CMD ["python3", "-u", "server.py"]
```

`server.py` mounts the disk before it starts serving, appends a line to a file on the disk for every request, and unmounts the disk when Cloud Run sends `SIGTERM`. It mounts with `--conditional` because more than one instance can have the disk mounted at once: Cloud Run scales out under load, and a new revision starts before the old one stops.

```python server.py theme={null}
import os
import signal
import subprocess
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from threading import Thread

MOUNTPOINT = "/mnt/archil"


class App(BaseHTTPRequestHandler):
    def do_GET(self):
        with open(f"{MOUNTPOINT}/visits.log", "a+") as log:
            log.write(f"Hello from {os.environ['K_REVISION']}\n")
            log.flush()
            os.fsync(log.fileno())
            log.seek(0)
            content = log.read().encode()
        self.send_response(200)
        self.send_header("Content-Length", str(len(content)))
        self.end_headers()
        self.wfile.write(content)


os.makedirs(MOUNTPOINT, exist_ok=True)
subprocess.run(["archil", "mount", os.environ["ARCHIL_DISK_ID"], MOUNTPOINT,
                "--region", os.environ["ARCHIL_REGION"], "--conditional"], check=True)

server = ThreadingHTTPServer(("0.0.0.0", int(os.environ.get("PORT", "8080"))), App)
signal.signal(signal.SIGTERM, lambda *_: Thread(target=server.shutdown).start())
server.serve_forever()
subprocess.run(["archil", "unmount", MOUNTPOINT], check=True)
```

Deploy from the directory that holds both files. Cloud Build builds the image, and `--execution-environment gen2` selects the environment that supports FUSE:

```bash theme={null}
gcloud run deploy archil-app --source . --region "$REGION" \
  --execution-environment gen2 \
  --set-env-vars "ARCHIL_DISK_ID=$ARCHIL_DISK_ID,ARCHIL_REGION=$ARCHIL_REGION" \
  --set-secrets ARCHIL_MOUNT_TOKEN=archil-mount-token:latest \
  --no-allow-unauthenticated
```

## Use the disk

Request the service with your own identity token:

```bash theme={null}
URL=$(gcloud run services describe archil-app --region "$REGION" --format 'value(status.url)')
curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" "$URL"
```

```text theme={null}
Hello from archil-app-00001-xyz
```

Deploy a new revision, for example after a code change, and request the service again. The new revision mounts the same disk and finds the line the first one wrote:

```bash theme={null}
gcloud run services update archil-app --region "$REGION" --update-env-vars RELEASE=2
curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" "$URL"
```

```text theme={null}
Hello from archil-app-00001-xyz
Hello from archil-app-00002-abc
```

## How the disk behaves on Cloud Run

**Mount with `--conditional`.** Every instance of every revision can then read and write anywhere on the disk. For faster writes to a directory only one instance uses, check it out with [`archil checkout`](https://docs.archil.com/reference/archil-cli#checkout); see [Sharing disks](https://docs.archil.com/concepts/sharing-disks.md).

**Unmount on `SIGTERM`.** Unmounting sends the client's buffered writes to the disk. Cloud Run sends `SIGTERM` before it shuts an instance down, and waits 10 seconds before stopping it.

## Clean up

Delete the service, the secret, the service's image, and the disk once you no longer need them:

```bash theme={null}
gcloud run services delete archil-app --region "$REGION"
gcloud secrets delete archil-mount-token
gcloud artifacts docker images delete "$REGION-docker.pkg.dev/$PROJECT/cloud-run-source-deploy/archil-app" --delete-tags
npx disk delete "$ARCHIL_DISK_ID"
```

## Next steps

* [Python SDK](https://docs.archil.com/sdks/python.md): read, write, and search the disk without mounting it.
* [Sharing disks](https://docs.archil.com/concepts/sharing-disks.md): how conditional mode and checkouts work.
* [`archil` CLI reference](https://docs.archil.com/reference/archil-cli): every `archil mount` option.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.