Skip to main content

Cyclos CloudRun Module — Configuration Guide

Cyclos is a professional banking and payment system designed for microfinance institutions, credit unions, complementary currency schemes, and community banks. This module deploys Cyclos on Google Cloud Run using the official cyclos/cyclos container image, backed by a managed Cloud SQL PostgreSQL instance.

Cyclos CloudRun is a wrapper module built on top of App CloudRun. It uses App CloudRun for all GCP infrastructure provisioning (Cloud Run service, networking, Cloud SQL, GCS, secrets, CI/CD) and adds Cyclos-specific application configuration on top.

Note: Variables marked as platform-managed are set and maintained by the platform. You do not normally need to change them.


How This Guide Is Structured

This guide documents only the variables that are unique to Cyclos_CloudRun or that have Cyclos-specific defaults that differ from the App_CloudRun base module. For all other variables — project identity, runtime scaling, storage, CI/CD, Redis, backup, custom SQL, networking, IAP, Cloud Armor, and VPC Service Controls — refer directly to the App_CloudRun Configuration Guide.

Variables fully covered by the App CloudRun guide:

Configuration AreaApp CloudRun SectionCyclos-Specific Notes
Module Metadata & ConfigurationGroup 0Different defaults for module_description and module_documentation. resource_creator_identity is the same — see Group 0.
Project & IdentityGroup 1Refer to base App CloudRun module documentation.
Runtime & ScalingGroup 3See Cyclos Runtime Configuration below for cpu_limit, memory_limit, and Cyclos-specific scaling defaults. container_image defaults to cyclos/cyclos; container_image_source defaults to prebuilt. enable_cloudsql_volume defaults to false (Cyclos uses TCP, not Unix socket).
Environment Variables & SecretsGroup 4See Cyclos Environment Variables below for SMTP defaults.
Observability & HealthGroup 5See Cyclos Health Probes below for renamed variables and Cyclos-specific defaults.
Jobs & Scheduled TasksGroup 6Refer to base App CloudRun module documentation.
CI/CD & GitHub IntegrationGroup 7Refer to base App CloudRun module documentation.
Storage — NFSGroup 8NFS is disabled by this module. See Platform-Managed Behaviours. enable_nfs defaults to false.
Storage — GCSGroup 9Refer to base App CloudRun module documentation.
Redis CacheGroup 10Redis is not supported by Cyclos CloudRun. enable_redis is hardcoded to false and is not exposed as a variable. Do not configure Redis for this module.
Backup & MaintenanceGroup 12Refer to base App CloudRun module documentation for backup_schedule and backup_retention_days. See Backup Import & Recovery below for enable_backup_import and related variables.
Custom Initialisation & SQLGroup 13Refer to base App CloudRun module documentation.
Access & NetworkingGroup 14Refer to base App CloudRun module documentation (ingress_settings, vpc_egress_setting, network_name).
Identity-Aware ProxyGroup 15Refer to base App CloudRun module documentation.
Cloud Armor & CDNGroup 16Refer to base App CloudRun module documentation.
VPC Service ControlsGroup 17Refer to base App CloudRun module documentation.

Platform-Managed Behaviours

The following behaviours are applied automatically by Cyclos CloudRun regardless of the variable values in your tfvars file. They cannot be overridden by user configuration.

BehaviourDetail
NFS disabledenable_nfs is forced to false in the application configuration. Cyclos stores uploaded files and media in the GCS bucket provisioned by the module (cyclos.storedFileContentManager = gcs), making a shared NFS filesystem unnecessary.
GCS file storagecyclos.storedFileContentManager = gcs is injected automatically. The GCS bucket name is derived from the deployment identifiers and injected as cyclos.storedFileContentManager.bucketName.
Schema managementcyclos.db.managed = true is set, allowing Cyclos to create and evolve its own database schema on startup. Do not run manual schema migrations against a Cyclos database managed this way.
PostgreSQL extensionsThe following extensions are automatically installed in the application database during the initialisation job: pg_trgm, uuid-ossp, cube, earthdistance, postgis, unaccent. These are required by Cyclos and are installed before the application starts.
Database initialisationA dedicated cyclos database user is created with the password from Secret Manager and granted the permissions required by the application. The postgres superuser is used only for the extension and user setup jobs.
TCP database connectionenable_cloudsql_volume defaults to false. Cyclos connects to Cloud SQL via direct TCP to the internal IP, not via the Cloud SQL Auth Proxy Unix socket. The DB_HOST environment variable is set to the Cloud SQL internal IP address automatically.

Cyclos Application Identity

These variables control how the Cyclos deployment is named and described. They correspond to the application_display_name and application_description variables in App CloudRun but use shorter names to match the Cyclos Common interface.

VariableDefaultOptions / FormatDescription & Implications
application_name"cyclos"[a-z][a-z0-9-]{0,19}Internal identifier used as the base name for the Cloud Run service, Artifact Registry repository, Secret Manager secrets, and GCS buckets. Functionally identical to application_name in App CloudRun. Do not change after initial deployment.
application_version"4.16.17"Cyclos version string, e.g. "4.16.17"Version tag applied to the container image and used for deployment tracking. Use the official Cyclos release version matching the image you intend to deploy. See the Cyclos release notes for available versions. When container_image_source = "prebuilt", this controls which tagged image is pulled from Docker Hub.
display_name"Cyclos Community Edition"Any stringHuman-readable name shown in the platform UI, the Cloud Run service list, and monitoring dashboards. Equivalent to application_display_name in App CloudRun. Can be updated freely without affecting resource names.
description"Cyclos Banking System on Cloud Run"Any stringBrief description of the deployment. Populated into the Cloud Run service description field and platform documentation. Equivalent to application_description in App CloudRun.

Validating Application Identity

# Confirm the Cloud Run service exists with the expected name
gcloud run services describe cyclos \
--region=REGION \
--format="table(metadata.name,metadata.annotations['run.googleapis.com/description'])"

Cyclos Runtime Configuration

Cyclos is a Java application and requires significantly more CPU and memory than a typical Cloud Run service. The module exposes cpu_limit and memory_limit as dedicated top-level variables rather than requiring users to set the full container_resources object.

VariableDefaultOptions / FormatDescription & Implications
cpu_limit"1000m"Cloud Run CPU quantity string (e.g. "1000m", "2000m")CPU limit for the Cyclos Cloud Run instance. Cyclos requires a minimum of 2 vCPU for reliable production operation. The default of 1000m is sufficient for low-traffic or development deployments. For production, set this to "2000m" or higher. Note: CPUs above "1000m" require cpu_always_allocated = true.
memory_limit"2Gi"Cloud Run memory quantity string (e.g. "2Gi", "4Gi")Memory limit for the Cyclos Cloud Run instance. 4 Gi is recommended for production. The JVM heap, Cyclos internal caches, and active sessions together typically consume 2–3 Gi under normal load.

Note on container_resources: Cyclos CloudRun exposes only cpu_limit and memory_limit as top-level variables. There is no container_resources variable in this module — the underlying object is assembled internally from these two variables via Cyclos Common. If you need to set CPU/memory requests or ephemeral storage limits, use Cyclos GKE (which does expose container_resources) or extend the module.

Cyclos-specific runtime defaults that differ from App CloudRun:

VariableApp CloudRun DefaultCyclos CloudRun DefaultReason
container_image_source"custom""prebuilt"The official cyclos/cyclos Docker Hub image is production-ready and pre-configured.
container_image"""cyclos/cyclos"The official Cyclos image from Docker Hub.
enable_cloudsql_volumetruefalseCyclos connects to Cloud SQL via direct TCP to the internal IP, not via the Unix socket provided by the Cloud SQL Auth Proxy.
min_instance_count01Cyclos requires at least one warm instance to avoid cold-start latency on banking transactions. Scale-to-zero is not recommended for production Cyclos deployments.
max_instance_count11Cyclos in standalone mode (cyclos.clusterHandler = none) should run as a single instance to avoid session inconsistency. Increase only after configuring Hazelcast clustering.

Validating Runtime Configuration

# View the CPU and memory limits on the latest revision
gcloud run services describe cyclos \
--region=REGION \
--format="yaml(spec.template.spec.containers[0].resources)"

# Confirm TCP database connection (DB_HOST should be an IP address)
gcloud run services describe cyclos \
--region=REGION \
--format="yaml(spec.template.spec.containers[0].env)" | grep DB_HOST

Cyclos Database Configuration

Cyclos requires PostgreSQL. The module uses db_name and db_user (shorter names aligned with the Cyclos_Common interface) in place of the application_database_name and application_database_user variables documented in App_CloudRun Group 11.

All other database variables (sql_instance_name, database_password_length, enable_auto_password_rotation, rotation_propagation_delay_sec, etc.) behave identically to the App_CloudRun equivalents — refer to App_CloudRun Group 11 for their documentation.

VariableDefaultOptions / FormatDescription & Implications
db_name"cyclos"[a-z][a-z0-9_]{0,62}The name of the PostgreSQL database created within the Cloud SQL instance. Injected as the DB_NAME environment variable. Do not change after initial deployment — Cyclos stores all application data in this database and renaming it requires manual migration.
db_user"cyclos"[a-z][a-z0-9_]{0,31}The PostgreSQL user created for the Cyclos application. Injected as the DB_USER environment variable. The password is auto-generated, stored in Secret Manager, and injected as DB_PASSWORD.

Important: Cyclos requires PostgreSQL. Cyclos CloudRun does not expose database_type as a user-facing variable — it is hardcoded to "POSTGRES_15" by Cyclos Common. Setting any MySQL or SQL Server database in the platform layer will prevent the application from starting.

PostgreSQL extensions are installed automatically — see Platform-Managed Behaviours. You do not need to set enable_postgres_extensions = true for the Cyclos-required extensions.

Validating Database Configuration

# Confirm the database and user were created
gcloud sql databases list --instance=INSTANCE_NAME --project=PROJECT_ID

gcloud sql users list --instance=INSTANCE_NAME --project=PROJECT_ID

# Confirm DB environment variables are injected into the Cloud Run service
gcloud run services describe cyclos \
--region=REGION \
--format="yaml(spec.template.spec.containers[0].env)" | grep -E "DB_"

Cyclos Environment Variables

The environment_variables variable (documented in App_CloudRun Group 4) has Cyclos-specific defaults that configure email delivery.

Default environment_variables in Cyclos CloudRun:

environment_variables = {
SMTP_HOST = ""
SMTP_PORT = "25"
SMTP_USER = ""
SMTP_PASSWORD = ""
SMTP_SSL = "false"
EMAIL_FROM = "cyclos@example.com"
}

Cyclos uses these variables to configure its outbound email transport (used for notifications, password resets, and transaction confirmations). Configure them to point to your SMTP server before going live. For sensitive values such as SMTP_PASSWORD, use secret_environment_variables instead:

environment_variables = {
SMTP_HOST = "smtp.sendgrid.net"
SMTP_PORT = "587"
SMTP_USER = "apikey"
SMTP_SSL = "true"
EMAIL_FROM = "noreply@yourbank.example.com"
}

secret_environment_variables = {
SMTP_PASSWORD = "cyclos-smtp-password" # Secret Manager secret name
}

All other environment_variables and secret_environment_variables behaviour is identical to App_CloudRun — refer to App_CloudRun Group 4.


Cyclos Health Probes

Cyclos is a Java application that performs database schema validation and migration on first boot. This startup phase can take 2–5 minutes on a fresh deployment, much longer than a typical Cloud Run service. The probe variables in Cyclos CloudRun use different names from App CloudRun (startup_probe and liveness_probe instead of startup_probe_config and health_check_config) and have extended default timeouts to accommodate this behaviour.

Both probes target the /api endpoint, which reflects the Cyclos application's readiness more accurately than a generic /healthz path.

VariableDefaultDescription & Implications
startup_probe{ enabled = true, type = "HTTP", path = "/api", initial_delay_seconds = 90, timeout_seconds = 30, period_seconds = 60, failure_threshold = 10 }Determines when the Cloud Run instance is ready to receive traffic after starting. The initial_delay_seconds = 90 gives the JVM time to start and Cyclos time to validate or create the database schema before the first probe fires. failure_threshold = 10 with period_seconds = 60 allows up to 11 minutes 30 seconds of additional startup time beyond the initial delay. This generous threshold is intentional — on first deployment the schema is created from scratch and startup can take several minutes.
liveness_probe{ enabled = true, type = "HTTP", path = "/api", initial_delay_seconds = 120, timeout_seconds = 10, period_seconds = 60, failure_threshold = 3 }Periodically checks whether a running Cyclos instance is healthy. The initial_delay_seconds = 120 prevents premature restarts during the startup phase. A period_seconds = 60 check interval is appropriate for a database-backed application — more frequent checks add unnecessary load to the database.

Relationship to App CloudRun probes: startup_probe maps to startup_probe_config in App CloudRun; liveness_probe maps to health_check_config. Their sub-field structure is identical. Cyclos CloudRun does not expose separate startup_probe_config or health_check_config variables — startup_probe and liveness_probe are the only probe variables in this module.

Validating Health Probe Configuration

Google Cloud Console: Navigate to Cloud Run → Services → cyclos → Revisions, select the latest revision, then click Container(s) and view the Health checks section.

# View startup and liveness probe config on the latest revision
gcloud run services describe cyclos \
--region=REGION \
--format="yaml(spec.template.spec.containers[0].livenessProbe,spec.template.spec.containers[0].startupProbe)"

# View Cloud Run logs for startup probe status
gcloud logging read \
"resource.type=cloud_run_revision AND resource.labels.service_name=cyclos AND severity>=WARNING" \
--project=PROJECT_ID \
--limit=20 \
--format="table(timestamp,severity,textPayload)"

Backup Import & Recovery

In addition to the scheduled backup (backup_schedule and backup_retention_days, documented in App_CloudRun Group 12), Cyclos_CloudRun supports a one-time import of an existing Cyclos database backup during deployment. This is designed for migrating an existing Cyclos instance to GCP or seeding a new environment with production data.

The backup import variables in Cyclos CloudRun have the same semantics as those in App CloudRun Group 12, with one naming difference: backup_uri (a full GCS object path or Google Drive file ID) is used instead of backup_file (a filename relative to the backup bucket).

VariableDefaultOptions / FormatDescription & Implications
enable_backup_importfalsetrue / falseWhen true, triggers a one-time Cloud Run Job to restore the backup specified by backup_uri from the source defined in backup_source. The import runs after the database is provisioned and extensions are installed. Configure backup_source, backup_uri, and backup_format before enabling. If the database already contains data, the import may produce errors — test in a non-production environment first.
backup_source"gcs"gcs / gdriveThe source from which the backup file is retrieved. gcs: imports from a Cloud Storage path. Provide the full GCS URI in backup_uri (e.g. gs://my-bucket/backups/cyclos.sql.gz). gdrive: imports from a Google Drive file. Provide the Drive file ID in backup_uri. Only used when enable_backup_import = true.
backup_uri""Full GCS URI or Google Drive file IDFor GCS: the full object URI, e.g. "gs://my-backup-bucket/cyclos-2024-01-15.sql.gz". For Google Drive: the file ID from the share URL (the string after /file/d/ in the URL). Required when enable_backup_import = true.
backup_format"gz"sql / tar / gz / tgz / tar.gz / zipThe format of the backup file. The default is "gz" (gzip-compressed SQL dump from pg_dump), which is the recommended format for Cyclos backups. Use "sql" for uncompressed plain-text dumps. Note: unlike Cyclos GKE, this module does not accept "auto" — the format must be specified explicitly.

Validating Backup Import

# Confirm the import job completed successfully
gcloud run jobs executions list \
--job=cyclos-backup-import \
--region=REGION \
--format="table(name,status.conditions[0].type,status.startTime,status.completionTime)"

# View import job logs for any errors
gcloud logging read \
"resource.type=cloud_run_job AND resource.labels.job_name=cyclos-backup-import" \
--project=PROJECT_ID \
--limit=50 \
--order=asc \
--format="table(timestamp,severity,textPayload)"

Deployment Prerequisites & Validation

After deploying Cyclos CloudRun, confirm the deployment is healthy:

# Confirm the Cloud Run service is deployed and its URL
gcloud run services describe cyclos \
--region=REGION \
--format="table(status.url,status.conditions[0].type)"

# View the latest revision status
gcloud run revisions list \
--service=cyclos \
--region=REGION \
--format="table(name,status.conditions[0].status,spec.containerConcurrency)"

# Confirm the GCS bucket provisioned for Cyclos file storage
gcloud storage buckets list \
--project=PROJECT_ID \
--filter="name:cyclos"

# Confirm PostgreSQL extensions were installed (via initialization job logs)
gcloud run jobs executions list \
--job=cyclos-db-init \
--region=REGION \
--format="table(name,status.conditions[0].type)"

# Verify the Cyclos API endpoint is responding
curl -s -o /dev/null -w "%{http_code}" https://SERVICE_URL/api
# Expect: 200

Configuration Pitfalls & Sensible Defaults

The table below identifies the variables most commonly misconfigured in Cyclos CloudRun deployments, explains the sensible starting value, and describes exactly what happens when the value is wrong.

Risk levels: Critical (data loss, full outage, security breach) — High (service unavailable or significant degradation) — Medium (degraded function or increased cost) — Low (minor impact).

VariableSensible DefaultRiskConsequence of Incorrect Value
application_name"cyclos" (default; do not change after first deploy)CriticalEmbedded in Cloud Run service name, Artifact Registry repo, Secret Manager secrets, and GCS bucket name (cyclos.storedFileContentManager.bucketName). Changing recreates all named resources — existing financial data, transaction history, and uploaded files become inaccessible.
tenant_deployment_idMatch environment: "prod", "staging", "dev"CriticalChanging after first deploy orphans the old Cloud SQL instance and GCS storage bucket. A new empty database is provisioned. All transaction data, accounts, and member records are left in the abandoned instance.
application_versionA pinned tag (e.g. "4.16.17"); never "latest" in productionCriticalCyclos database schema migrations are one-way and version-specific. Deploying a newer version without a tested migration path can leave the schema in an inconsistent state. Always test version upgrades in a staging environment first.
memory_limit"4Gi" (default) — never reduce below 2GiCriticalCyclos is a Java application that defaults to consuming available heap. Too little memory: the JVM cannot complete garbage collection and throws OutOfMemoryError, causing the container to crash. Cloud Run OOMKills the instance (exit code 137). Minimum recommended: 2Gi for light workloads, 4Gi for production.
CYCLOS_OPTIONS (via environment_variables)"-Xmx512m" minimum; "-Xmx2g" for production — set via environment_variables = { CYCLOS_OPTIONS = "-Xmx2g" }CriticalNo JVM heap limit set: Cyclos uses all available container memory, leaving no room for the OS and JVM metaspace. Under load the container is OOMKilled. Always set -Xmx to 75–80% of memory_limit (e.g. -Xmx3g for a 4 Gi limit).
database_type"POSTGRES_15" (hardcoded in Cyclos Common; PostgreSQL is the only supported database)CriticalCyclos requires PostgreSQL with multiple extensions (pg_trgm, uuid-ossp, cube, earthdistance, postgis, unaccent). These extensions are provisioned automatically by the db-init job. Do not change database_type — MySQL and SQL Server are not supported by Cyclos.
cyclos.storedFileContentManager env var"gcs" (hardcoded in Cyclos Common)CriticalOverriding to "local" via environment_variables: Cyclos writes uploaded files to ephemeral container storage instead of GCS. All files (profile photos, transaction attachments) are lost on instance restart or scale event. Do not override this value.
cyclos.storedFileContentManager.bucketName env varAuto-derived as <resource_prefix>-cyclos-storageCriticalPointing to a non-existent GCS bucket: all Cyclos file operations fail. Profile photos cannot be uploaded or displayed. Transaction attachments fail to save. Ensure the bucket is provisioned before first deploy and the name matches exactly.
enable_nfsfalse (enforced by Cyclos CloudRun; Cyclos uses GCS for file storage, not NFS)LowSetting enable_nfs = true via environment_variables override has no effect — Cyclos CloudRun explicitly sets enable_nfs = false regardless of user input. NFS is not used by Cyclos.
enable_cloudsql_volumefalse (default for Cyclos CloudRun — connects via TCP to internal IP)Highfalse requires Cloud SQL to be reachable via private IP on the VPC. If Private Service Access or VPC peering is not configured, all DB connections fail at startup. Cyclos's db-init job also fails, preventing schema initialization.
min_instance_count1 (default; Cyclos is stateful and benefits from a warm instance)High0 (scale-to-zero): Cyclos JVM startup time is 45–120 seconds depending on database size and extension loading. Cold-start requests time out before Cyclos is ready. The startup probe must be tuned (failure_threshold ≥ 10) to allow sufficient time.
max_instance_count1 (default; Cyclos Community Edition is not horizontally scalable without licensing)Critical> 1 without a Cyclos enterprise license that permits clustering: multiple instances compete for the same database without coordination. Transaction processing logic (e.g. balance checks) becomes non-atomic, causing data corruption. Keep max_instance_count = 1 unless you have a clustered Cyclos license.
container_resources{ cpu_limit = "2000m", memory_limit = "4Gi" } (default)HighCPU too low: Cyclos's Java runtime is CPU-bound during startup and under heavy transaction load. With < 1000m CPU, startup can exceed 3 minutes, causing the startup probe to fail.
startup_probe.failure_threshold10 (default; ~11.5 min total tolerance at 60 s periods)HighToo low: Cyclos must run the db-init job (creates PostgreSQL extensions, which can take 1–2 min on fresh Cloud SQL) before the main container starts. If the probe fires before extensions are installed, the container crashes on its first DB connection. Keep failure_threshold ≥ 10 with period_seconds = 60.
startup_probe.path"/api" (Cyclos REST API root)CriticalWrong path: Cloud Run's startup probe always fails, preventing the revision from ever becoming healthy. "/api" returns 200 OK once Cyclos is fully initialised. Do not use "/" — it redirects to the web UI with a 302.
db_name"cyclos" (default; do not change after first deploy)CriticalRenaming creates a new empty database. The db-init job runs against the new (empty) database. All transaction history remains in the old (unreferenced) database.
db_user"cyclos" (default; do not change after first deploy)HighChanging creates a new DB user without grants. Cyclos cannot authenticate until db-init re-runs grants. Causes an outage until grants are applied.
execution_environment"gen2" (required for VPC egress to reach Cloud SQL private IP)High"gen1": Cloud SQL Auth Proxy socket-based auth is not available, and some VPC routing configurations used by private Cloud SQL don't work reliably in gen1. Use gen2 for all Cyclos deployments.
secret_environment_variablesUse for ROOT_PASSWORD and DB_PASSWORD (auto-managed by db-init)HighDB credentials in plain environment_variables: visible in the Cloud Run revision configuration in the GCP Console and in Terraform state in plaintext.
enable_backup_importfalse after a successful restoreHighLeaving true: the restore job re-runs on every tofu apply, overwriting the live Cyclos financial database with the stale backup. All transactions, balances, and member data entered since the backup are destroyed.
binauthz_evaluation_mode"ALWAYS_ALLOW" until CI pipeline attests imagesCritical"REQUIRE_ATTESTATION" without a working attestation pipeline: no new Cyclos image can be deployed. Rollbacks also fail.
enable_vpc_scfalse until perimeter is validated; use vpc_sc_dry_run = true firstCriticalenable_vpc_sc = true with vpc_sc_dry_run = false: if the Cyclos Cloud Run SA is absent from the access level, Cloud SQL and Secret Manager access fails immediately. Complete outage.

Destroying Resources

Known Deletion Issue: Serverless IPv4 Address Release

When destroying a Cloud Run deployment, you may encounter an error similar to:

Error: Error waiting for Subnetwork to be deleted: The following serverless IPv4 address(es) on subnet ... are still in use.

Cause: GCP holds serverless IPv4 addresses on the VPC subnet asynchronously after a Cloud Run service is deleted. These addresses are released by GCP approximately 20–30 minutes after the Cloud Run service is removed. Terraform/OpenTofu cannot complete the subnet or VPC deletion until they are fully released.

Resolution: Wait 20–30 minutes after the initial destroy attempt, then re-run the destroy command:

tofu destroy

The second run will succeed once GCP has released the reserved addresses.