Radicale on Cloud Run — Lab Guide
Overview
Estimated time: 30–60 minutes
Radicale is an open-source, self-hosted CalDAV/CardDAV server for calendar and contacts sync. This lab takes you through the full operational lifecycle of the Radicale on Cloud Run module on Google Cloud: deploy it, access and verify it, run it day-to-day, observe it, diagnose common problems, and tear it down.
The lab focuses on operating the Cloud Run module and the Google Cloud platform, not on Radicale product features. For the complete list of provisioned services and every configuration input (organised by group), see the Configuration Guide — this lab deliberately does not duplicate that detail so it stays accurate over time.
Objectives
By the end of this lab you will be able to:
- Deploy the module from the RAD platform and locate the resources it provisions.
- Access and verify the running service, retrieve the generated admin credential, and connect a CalDAV/CardDAV client.
- Understand why Cloud Run cannot create NEW collections via a standard client, and where the pre-seeded defaults come from.
- Perform day-2 operations — inspect, scale, and update.
- Observe the service with Cloud Logging and Cloud Monitoring.
- Diagnose and resolve the most common deployment and runtime issues.
- Tear the deployment down cleanly.
Prerequisites
- Services_GCP deployed in the target project (provides the VPC, Artifact Registry, and shared service accounts this module depends on).
- A Google Cloud project with billing enabled.
- gcloud CLI authenticated:
gcloud auth loginandgcloud auth application-default login. - Project Owner (or equivalent) IAM on the project.
- RAD platform access with permission to deploy modules into the project.
- (Optional) A CalDAV/CardDAV client to verify end-to-end sync — e.g. Thunderbird, Apple Calendar/Contacts, or DAVx5.
Set these shell variables once; every task below reuses them:
export PROJECT="<your-gcp-project-id>"
export REGION="us-central1" # the region you deploy into
Task 1 — Deploy the module [Automated]
-
In the RAD platform, open Radicale (Cloud Run), set
project_id, and review the inputs. Configure only what you need — the Configuration Guide documents every input by group, with defaults. Setapplication_display_name = "Radicale"explicitly — the module's default currently carries a stale value inherited from its clone source (see the Configuration Guide's Pitfalls section). Review the estimated cost (if credits are enabled) and click Deploy, which opens the deployment status page with real-time logs. -
The platform provisions the Cloud Run service, a
storageGCS bucket mounted at/var/lib/radicale, a Secret Manager secret holding a generatedADMIN_PASSWORD, and runs theseed-default-collectionsinitialization job that writes a default calendar and address book onto the storage volume. First deploys typically take 5–10 minutes — much faster than database-backed modules, since there is no Cloud SQL instance to provision. -
When it completes, discover the resources with name-agnostic filters:
SERVICE=$(gcloud run services list --project="$PROJECT" --region="$REGION" \
--filter="metadata.name~radicale" --format="value(metadata.name)" --limit=1)
SERVICE_URL=$(gcloud run services describe "$SERVICE" \
--project="$PROJECT" --region="$REGION" --format="value(status.url)")
echo "Service: $SERVICE"
echo "URL: $SERVICE_URL"
Task 2 — Access & verify [Manual]
-
Confirm the service is healthy and serving (expect a
302redirect to the web UI, since/is unauthenticated by design):curl -s "$SERVICE_URL/" -o /dev/null -w '%{http_code}\n' # expect 302 -
Radicale ships with no built-in default admin account — retrieve the generated credential from Secret Manager:
SECRET=$(gcloud secrets list --project="$PROJECT" --filter="name~radicale-admin-password" \
--format="value(name)" --limit=1)
ADMIN_PASSWORD=$(gcloud secrets versions access latest --secret="$SECRET" --project="$PROJECT")
echo "Username: admin"
echo "Password: $ADMIN_PASSWORD" -
Confirm authenticated access works with a
PROPFINDagainst the admin's principal (expect207 Multi-Status):curl -s -u "admin:$ADMIN_PASSWORD" -X PROPFIND "$SERVICE_URL/admin/" \
-H "Depth: 1" -o /dev/null -w '%{http_code}\n' -
Connect a CalDAV/CardDAV client (Thunderbird, Apple Calendar, DAVx5) to
$SERVICE_URL/admin/using theadminusername and the retrieved password. You should see the pre-seeded Default Calendar and Default Address Book — these were created automatically by theseed-default-collectionsjob at deploy time (see Task 5 for why this job exists).
Task 3 — Operate & keep it running (Day-2) [Manual]
-
Inspect the service and its revisions:
gcloud run services describe "$SERVICE" --project="$PROJECT" --region="$REGION"
gcloud run revisions list --service="$SERVICE" --project="$PROJECT" --region="$REGION" -
Scale —
max_instance_countis pinned to1and should not be raised: Radicale's storage backend is not designed for concurrent multi-instance access.min_instance_countcan be raised to1via the RAD platform's Update flow if you want to avoid cold starts, at the cost of an always-on instance. -
Update the application version tag via the RAD platform's Update flow. Remember: Radicale's container registry tags have no
vprefix (e.g.3.7.7, notv3.7.7). -
Manage secrets:
gcloud secrets list --project="$PROJECT" --filter="name~radicale"
gcloud run jobs list --project="$PROJECT" --region="$REGION" -
Inspect the storage bucket holding every collection:
BUCKET=$(gcloud storage buckets list --project="$PROJECT" --filter="name~radicale" \
--format="value(name)" --limit=1)
gcloud storage ls "gs://$BUCKET/collections/collection-root/admin/"
Task 4 — Observe: Logging & Monitoring [Manual]
-
Logs:
gcloud run services logs read "$SERVICE" --project="$PROJECT" --region="$REGION" --limit=50 -
Monitoring — open the Cloud Run dashboard for the service and review request count, latency, instance count, and CPU/memory utilisation. The module can provision an uptime check (disabled by default); if enabled, confirm it is green under Monitoring → Uptime checks.
Task 5 — Troubleshoot & debug [Manual]
-
Revision unhealthy / service won't serve: inspect the latest revision and its logs. The startup probe targets
/.gcloud run revisions list --service="$SERVICE" --project="$PROJECT" --region="$REGION"
gcloud run services logs read "$SERVICE" --project="$PROJECT" --region="$REGION" --limit=100 -
seed-default-collectionsjob failed, or no default calendar shows up:gcloud run jobs executions list --job="${SERVICE}-seed-default-collections" --project="$PROJECT" --region="$REGION"Confirm the
storageGCS bucket exists and the job's execution succeeded. -
A CalDAV/CardDAV client (or Radicale's own web UI) fails to create a NEW calendar with a generic error, "Bad Request", or similar. This is expected behaviour on Cloud Run, not a bug in your client. Creating a new collection requires the WebDAV
MKCOLmethod, and Google's Cloud Run frontend (GFE) rejectsMKCOLat the edge — the request never reaches the Radicale container. Confirm with:curl -s -u "admin:$ADMIN_PASSWORD" -X MKCOL "$SERVICE_URL/admin/a-new-calendar/" -o /dev/null -w '%{http_code}\n'
# expect a Google frontend error (400), NOT a Radicale responseYou cannot work around this from the client side, and Cloud Run has no shell access for a manual fix. Use the pre-seeded Default Calendar/Address Book, add a custom
initialization_jobsentry to seed more collections at deploy time, or switch toRadicale_GKE, whose LoadBalancer Service has no such restriction. -
401 Unauthorized on every request, even with the right-looking credential: double-check you retrieved the current
ADMIN_PASSWORDfrom Secret Manager — the value regenerates only when the secret itself changes, but a stale, manually-copied password will not match. Also confirm you're usingadmin(or your configuredADMIN_USERNAME), not an email address — Radicale's htpasswd auth expects a plain username. -
403 / permission errors: verify the runtime service account's IAM roles.
See the Configuration Guide's Configuration Pitfalls section for setting-specific gotchas.
Task 6 — Tear down [Automated]
On the Deployments page, open the deployment and click the Trash icon
(Delete). Delete runs terraform destroy and is irreversible. If a
deployment is stuck and the RAD platform can no longer manage it, use
Purge instead — it removes the deployment from RAD's records without
destroying the cloud resources. This removes everything the module created —
the Cloud Run service, the storage GCS bucket (and every calendar/address
book it held), Secret Manager secrets, and Artifact Registry images.
Resources owned by Services_GCP (the VPC, registry) are managed
separately and are not removed here.
Summary
| Task | Type | Outcome |
|---|---|---|
| 1 — Deploy | Automated | Module provisions Cloud Run, a GCS storage bucket, a generated admin secret, and runs the default-collection seed job |
| 2 — Access & verify | Manual | 302 on /; retrieve the generated admin password; connect a CalDAV/CardDAV client and see the seeded defaults |
| 3 — Operate | Manual | Inspect revisions, understand the max=1 scaling limit, update version, inspect storage |
| 4 — Observe | Manual | Query Cloud Logging; review Cloud Monitoring metrics and uptime check |
| 5 — Troubleshoot | Manual | Diagnose revision, seed-job, MKCOL/Cloud-Run-edge, and auth issues |
| 6 — Tear down | Automated | Delete (Trash) removes all module resources, including every stored collection |