Certification track: Associate Cloud Engineer (ACE)
Invoice Ninja on Google Cloud Run
This document provides a comprehensive reference for the modules/InvoiceNinja_CloudRun module. It covers architecture, IAM, configuration variables, Invoice Ninja-specific behaviours, and operational patterns for deploying Invoice Ninja on Google Cloud Run (v2).
1. Module Overview
Invoice Ninja is a professional open-source invoicing and billing platform used by freelancers and small businesses. It provides quotes, invoices, receipts, client payments, recurring billing, expense tracking, time tracking, project management, and a self-service client portal. InvoiceNinja CloudRun is a wrapper module built on top of App CloudRun. It uses App CloudRun for all GCP infrastructure provisioning and injects Invoice Ninja-specific application configuration, database initialisation, and storage configuration via InvoiceNinja Common.
Key Capabilities:
- Compute: Cloud Run v2 (Gen2), Laravel PHP + nginx container, 2 vCPU / 2 Gi by default.
min_instance_count = 0by default (scale-to-zero, same as theApp_CloudRunfoundation default) — override to1to keep an instance warm and avoid cold-start latency on invoice processing. - Data Persistence: Cloud SQL MySQL 8.0 (not PostgreSQL). GCS volumes via GCS Fuse for shared invoice PDFs, client logos, and uploaded documents. NFS optionally enabled for shared persistent storage.
- PDF Generation: Invoice Ninja bundles snappdf (headless Chromium) at
/usr/local/bin/chrome. PDF generation is CPU and memory intensive — 2 vCPU / 2 Gi is the recommended minimum. - Security: Inherits Cloud Armor WAF, IAP, Binary Authorization, and VPC Service Controls from
App CloudRun. TheAPP_KEYsecret is auto-generated byInvoiceNinja Commonand stored in Secret Manager. - Queue & Cache: Redis enabled by default (
enable_redis = true) — Invoice Ninja uses Redis forQUEUE_CONNECTION,CACHE_DRIVER, andSESSION_DRIVER. Background PDF generation and email queuing require Redis in production. - CI/CD: Prebuilt
invoiceninja/invoiceninja:5image deployed by default (container_image_source = 'prebuilt'). Cloud Build custom image pipeline available. - Reliability: Health probes target
/with a generous 90-second initial delay and 30-retry failure threshold to accommodate Invoice Ninja's PHP initialisation and first-boot database migrations.
Project & Application Identity
| Variable | Group | Type | Default | Description |
|---|---|---|---|---|
project_id | 1 | string | — | GCP project ID. Required. |
tenant_deployment_id | 2 | string | 'demo' | Short suffix appended to all resource names. |
support_users | 2 | list(string) | [] | Email recipients for monitoring alerts. |
resource_labels | 2 | map(string) | {} | Labels applied to all provisioned resources. |
application_name | 3 | string | 'invoiceninja' | Base resource name. Do not change after initial deployment. |
application_display_name | 3 | string | 'Invoice Ninja' | Human-readable name shown in the GCP Console. |
application_description | 3 | string | 'Invoice Ninja - Open-source invoicing platform' | Cloud Run service description. |
application_version | 3 | string | '5' | Invoice Ninja image version tag. Increment to deploy a new release. |
Wrapper architecture: InvoiceNinja CloudRun calls InvoiceNinja Common to build an application_config object containing Invoice Ninja-specific environment variables, probe configuration, and the db-init and artisan-migrate job definitions. The module injects DB_CONNECTION=mysql and the snappdf PDF generator configuration automatically. module_storage_buckets carries the GCS storage bucket provisioned by InvoiceNinja Common. scripts_dir is resolved to abspath("${module.invoiceninja_app.path}/scripts") at apply time.
MySQL note: Invoice Ninja requires MySQL 8.0, not PostgreSQL. database_type = "MYSQL_8_0" is the default and should not be changed.
2. IAM & Access Control
InvoiceNinja_CloudRun delegates all IAM provisioning to App_CloudRun. The Cloud Run SA, Cloud Build SA, IAP service agent, and password rotation role sets are identical to those in App_CloudRun.
Application secret: InvoiceNinja Common auto-generates the APP_KEY secret (Laravel application encryption key — a base64-encoded 32-byte random value, stored as base64:<value>) in Secret Manager and exposes it both via its own secret_ids output and embedded in config.secret_environment_variables. The InvoiceNinja_CloudRun variant, however, does not consume either of those directly for the service container — it computes the same secret ID itself (secret-<resource_prefix>-<app>-app-key) into its own module_secret_env_vars local, specifically to avoid a Terraform "known after apply" dependency cycle that would otherwise occur when the secret is replaced. The net effect is the same: APP_KEY reaches the container only as a Secret Manager reference, never as a plaintext environment variable.
Database initialisation identity: The db-init and artisan-migrate Cloud Run Jobs run under the Cloud Run SA. They connect to Cloud SQL MySQL via the Auth Proxy Unix socket (since enable_cloudsql_volume = true by default), using DB_HOST (the socket path under /cloudsql), DB_USER, and ROOT_PASSWORD (from Secret Manager).
120-second IAM propagation delay: Inherited from App CloudRun — the Invoice Ninja service is not deployed until the delay completes, preventing secret-read failures on the first revision start.
3. Core Service Configuration
A. Compute (Cloud Run)
Invoice Ninja is a Laravel PHP application with bundled Chromium for PDF generation. Both components impose significant resource requirements. InvoiceNinja CloudRun exposes cpu_limit and memory_limit as dedicated top-level variables.
Scale-to-zero is enabled by default (min_instance_count = 0, matching the App_CloudRun foundation default). Invoice Ninja's cold start involves PHP-FPM initialisation, Laravel bootstrap, and optional database migration — this can take 20–40 seconds. Set min_instance_count = 1 to keep an instance warm and avoid this cold-start latency in production.
Startup CPU Boost is always enabled (hardcoded in App CloudRun).
Container image: container_image_source defaults to 'prebuilt', deploying the official invoiceninja/invoiceninja:5 image directly. Set to 'custom' to build a modified image using Cloud Build with a custom Dockerfile from InvoiceNinja_Common.
| Variable | Group | Default | Description |
|---|---|---|---|
deploy_application | 4 | true | Set false for infrastructure-only deployment (SQL, storage, secrets). |
container_image_source | 4 | 'prebuilt' | 'prebuilt' deploys the official image. 'custom' builds via Cloud Build. |
container_image | 4 | "" | Override image URI. Leave empty for the default Invoice Ninja image. |
min_instance_count | 4 | 0 | Minimum running instances (scale-to-zero by default). Set 1 to avoid cold-start latency on invoice processing. |
max_instance_count | 4 | 3 | Maximum running instances. Acts as a cost ceiling. |
cpu_limit | 4 | '2000m' | CPU per instance. 2 vCPU minimum for PDF generation with Chromium. |
memory_limit | 4 | '2Gi' | Memory per instance. 2 Gi minimum for Chromium; 4 Gi recommended for production. |
container_port | 4 | 80 | Invoice Ninja listens on port 80 via nginx. |
execution_environment | 4 | 'gen2' | Gen2 required for NFS mounts and GCS Fuse. |
timeout_seconds | 4 | 300 | Max request duration. Increase for PDF generation or report exports. |
enable_cloudsql_volume | 4 | true | Mounts Cloud SQL Auth Proxy socket. |
cpu_always_allocated | 4 | false | Request-based billing by default (cost-first, pairs with min_instance_count=0). Trade-off: the Laravel queue/scheduler stops when idle, so recurring invoices and scheduled emails don't fire — externalise them via Cloud Scheduler hitting the cron endpoint, or set true with min_instance_count >= 1 for continuous operation. |
traffic_split | 4 | [] | Percentage-based canary/blue-green traffic allocation. |
service_annotations | 4 | {} | Advanced Cloud Run annotations. |
service_labels | 4 | {} | Labels applied to the Cloud Run service. |
Differences from App CloudRun defaults:
| Variable | App CloudRun | InvoiceNinja CloudRun | Reason |
|---|---|---|---|
container_port | 8080 | 80 | Invoice Ninja uses nginx on port 80. |
cpu_limit | '1000m' | '2000m' | snappdf Chromium PDF generation requires ≥2 vCPU for reliable operation. |
memory_limit | '512Mi' | '2Gi' | Chromium process requires 1–2 Gi on top of the PHP-FPM memory footprint. |
container_image_source | 'custom' | 'prebuilt' | The official invoiceninja/invoiceninja image is production-ready without customisation. |
enable_image_mirroring | false | true | Invoice Ninja mirrors its base image to Artifact Registry by default. |
B. Database (Cloud SQL — MySQL 8.0)
Invoice Ninja requires MySQL 8.0. PostgreSQL, SQL Server, and other engines are unsupported and will cause Invoice Ninja to fail at startup with a database driver error.
Unix socket connection: enable_cloudsql_volume defaults to true. App CloudRun injects the Auth Proxy sidecar and sets DB_HOST to the socket path under /cloudsql. Invoice Ninja reads this via the standard DB_* environment variables.
| Variable | Group | Default | Description |
|---|---|---|---|
database_type | 12 | 'MYSQL_8_0' | Cloud SQL engine. Must remain MYSQL_8_0 — Invoice Ninja does not support PostgreSQL. |
application_database_name | 12 | 'invoiceninja' | MySQL database name. Do not change after initial deployment. |
application_database_user | 12 | 'invoiceninja' | MySQL application user. Password auto-generated and stored in Secret Manager. |
database_password_length | 12 | 32 | Auto-generated password length. Range: 16–64. |
enable_auto_password_rotation | 12 | false | Automated zero-downtime password rotation. |
rotation_propagation_delay_sec | 12 | 90 | Seconds to wait after rotation before restarting the service. |
db_nameanddb_userare passed through toInvoiceNinja Commonfor injection into theapplication_config. These align withapplication_database_nameandapplication_database_userin the Cloud SQL provisioning path — both should be set to the same value.
C. Storage (NFS & GCS)
NFS is enabled by default (enable_nfs = true). Invoice Ninja stores uploaded documents, client logos, invoice PDFs, and other assets on the NFS share so that all Cloud Run instances access a consistent filesystem. Requires execution_environment = 'gen2'.
GCS storage bucket: A data GCS bucket is provisioned by default via storage_buckets. GCS Fuse volumes can be configured separately to mount buckets directly into the container for document storage.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_nfs | 11 | true | Provisions NFS for shared document and file storage. Requires gen2. |
nfs_mount_path | 11 | '/mnt/nfs' | Container path where the NFS share is mounted. |
create_cloud_storage | 11 | true | Set false to skip GCS bucket creation. |
storage_buckets | 11 | [{ name_suffix = "data" }] | GCS buckets to provision. |
gcs_volumes | 11 | [] | GCS buckets to mount via GCS Fuse (requires gen2). Each entry: name, bucket_name, mount_path, readonly, mount_options. |
manage_storage_kms_iam | 11 | false | Creates a CMEK KMS keyring/key and enables CMEK on all storage buckets. |
enable_artifact_registry_cmek | 11 | false | Creates Artifact Registry KMS key for at-rest image encryption. |
D. Networking
Cloud Run uses Direct VPC Egress to reach Cloud SQL's internal IP. Because enable_cloudsql_volume = true is the default, the Auth Proxy sidecar handles the Cloud SQL connection via Unix socket. Invoice Ninja needs outbound internet access for SMTP email delivery — ensure vpc_egress_setting = 'PRIVATE_RANGES_ONLY' to allow direct public egress.
| Variable | Group | Default | Description |
|---|---|---|---|
ingress_settings | 5 | 'all' | 'all' — public internet; 'internal' — VPC only; 'internal-and-cloud-load-balancing' — forces traffic through the HTTPS Load Balancer. |
vpc_egress_setting | 5 | 'PRIVATE_RANGES_ONLY' | Routes only RFC 1918 traffic via VPC. 'ALL_TRAFFIC' routes all egress via VPC. |
E. Initialization & Bootstrap
Two Cloud Run Jobs are provisioned by InvoiceNinja Common when initialization_jobs is left as the default empty list ([]):
1. db-init — Uses the mysql:8.0-debian image. Idempotent operations:
- Connects to Cloud SQL MySQL via the Auth Proxy Unix socket.
- Creates the
invoiceninjadatabase user with the password from Secret Manager. - Creates the
invoiceninjadatabase if it does not exist. - Grants the
invoiceninjauser full privileges on the database.
2. artisan-migrate — Uses the Invoice Ninja application image. Runs Laravel database migrations including initial schema creation and seed data (artisan migrate --seed). Depends on db-init completing successfully. execute_on_apply = true so it runs on every deployment to apply schema changes from version upgrades.
Additional recurring cron jobs can be defined via cron_jobs:
| Variable | Group | Default | Description |
|---|---|---|---|
initialization_jobs | 13 | [] | One-shot Cloud Run Jobs. Leave empty for InvoiceNinja Common to supply the default db-init and artisan-migrate jobs. |
cron_jobs | 13 | [] | Recurring jobs triggered by Cloud Scheduler. |
4. Advanced Security
A. Cloud Armor WAF
When enable_cloud_armor = true, a Global HTTPS Load Balancer with a Cloud Armor WAF policy (OWASP Top 10, adaptive DDoS, 500 req/min rate limiting) is provisioned in front of Cloud Run.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_cloud_armor | 10 | false | Provisions Global HTTPS LB + Cloud Armor WAF. Required for custom domains, CDN, and DDoS protection. |
admin_ip_ranges | 10 | [] | CIDR ranges exempted from WAF rules (e.g., office VPN, CI/CD egress IPs). |
B. Identity-Aware Proxy (IAP)
When enable_iap = true, Cloud Run's native IAP integration is enabled directly on the service. Useful for restricting Invoice Ninja access to authenticated employees in internal billing workflows.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_iap | 5 | false | Enables IAP natively on the Cloud Run service. |
iap_authorized_users | 5 | [] | Users/service accounts granted access. Format: 'user:email' or 'serviceAccount:sa@...'. |
iap_authorized_groups | 5 | [] | Google Groups granted access. Format: 'group:name@example.com'. |
C. Binary Authorization
When enable_binary_authorization = true, Cloud Run enforces that deployed images carry a valid cryptographic attestation.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_binary_authorization | 8 | false | Enforces image attestation. Requires a Binary Authorization policy and attestor pre-configured in the project. |
D. VPC Service Controls
When enable_vpc_sc = true, all GCP API calls from this module are bound within an existing VPC-SC perimeter.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_vpc_sc | 22 | false | Registers module API calls within the project's VPC-SC perimeter. A perimeter must already exist before enabling. |
E. Secret Manager Integration
Invoice Ninja secrets are stored in Secret Manager and injected natively by Cloud Run at revision start — plaintext is never written to state.
Auto-generated secrets: InvoiceNinja Common generates the APP_KEY (Laravel encryption key) and injects it via secret_environment_variables. The DB_PASSWORD and ROOT_PASSWORD secrets are provisioned automatically by App CloudRun.
| Variable | Group | Default | Description |
|---|---|---|---|
secret_environment_variables | 6 | {} | Map of env var name → Secret Manager secret ID. Resolved at runtime. (e.g., { MAIL_PASSWORD = "smtp-password-secret" }) |
secret_rotation_period | 6 | '2592000s' | Frequency at which Secret Manager emits rotation notifications. Default: 30 days. |
secret_propagation_delay | 6 | 30 | Seconds to wait after secret creation before dependent resources proceed. |
5. Traffic & Ingress
A. HTTPS Load Balancer
When enable_cloud_armor = true, a Global HTTPS Load Balancer backed by a Serverless NEG is provisioned. Traffic flows: Internet → Cloud Armor → Global HTTPS LB → Serverless NEG → Cloud Run.
Setting ingress_settings = 'internal-and-cloud-load-balancing' forces all Invoice Ninja traffic through the LB, preventing direct *.run.app URL access.
B. Cloud CDN
When enable_cdn = true (requires enable_cloud_armor = true), Cloud CDN is attached to the HTTPS Load Balancer backend. Invoice Ninja serves a mix of static assets and dynamic invoice data. Cloud CDN is well-suited for Invoice Ninja's CSS, JS, and logo assets. Ensure invoice PDF and client portal responses include appropriate Cache-Control headers before enabling CDN for API paths.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_cdn | 10 | false | Enables Cloud CDN on the HTTPS LB backend. Only effective when enable_cloud_armor = true. |
max_images_to_retain | 10 | 7 | Maximum recent container images to keep in Artifact Registry. |
delete_untagged_images | 10 | true | Automatically deletes untagged (dangling) images from Artifact Registry. |
image_retention_days | 10 | 30 | Days after which images are eligible for deletion. |
C. Custom Domains
Custom domains are attached to the Global HTTPS Load Balancer via application_domains. Google-managed SSL certificates are provisioned automatically. DNS must point to the load balancer IP after apply.
| Variable | Group | Default | Description |
|---|---|---|---|
application_domains | 10 | [] | Custom domain names for the HTTPS LB. Google-managed SSL certificates provisioned per domain. (e.g., ['invoices.example.com']) |
6. CI/CD & Delivery
A. Cloud Build Triggers
When enable_cicd_trigger = true, a Cloud Build GitHub connection and push trigger are provisioned. Since container_image_source = 'prebuilt' is the default, CI/CD triggers are most useful when switching to 'custom' to build a modified Invoice Ninja image with company-specific theme files or plugins.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_cicd_trigger | 8 | false | Provisions a Cloud Build GitHub trigger. |
github_repository_url | 8 | "" | Full HTTPS URL of the GitHub repository. |
github_token | 8 | "" | GitHub PAT (repo, admin:repo_hook scopes). Required on first apply. Sensitive. |
github_app_installation_id | 8 | "" | GitHub App installation ID (preferred for organisation repos). |
cicd_trigger_config | 8 | { branch_pattern = "^main$" } | Advanced trigger config: branch_pattern, included_files, ignored_files, trigger_name, substitutions. |
B. Cloud Deploy Pipeline
When enable_cloud_deploy = true (requires enable_cicd_trigger = true), the CI/CD pipeline is upgraded to a managed Cloud Deploy delivery pipeline with sequential promotion stages.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_cloud_deploy | 8 | false | Provisions a Cloud Deploy pipeline. Requires enable_cicd_trigger = true. |
cloud_deploy_stages | 8 | [dev, staging, prod(approval)] | Ordered promotion stages. Each: name, target_name, service_name, require_approval, auto_promote. |
7. Reliability & Scheduling
A. Scaling & Concurrency
min_instance_count = 0 by default (scale-to-zero, matching the App_CloudRun foundation default) — set to 1 to keep an instance warm at all times. max_instance_count = 3 provides horizontal scale during peak invoice processing or batch export periods. Both values are user-configurable.
Invoice Ninja uses Redis-backed queuing, so multiple instances can process different queue jobs without contention. Sessions are stored in Redis when enable_redis = true, making horizontal scaling safe.
B. Traffic Splitting
Traffic splitting is supported via traffic_split. Invoice Ninja's Redis-backed sessions and queues are shared across instances, making canary deployments safe — existing client sessions persist regardless of which instance handles subsequent requests.
C. Health Probes & Uptime Monitoring
Invoice Ninja does not expose a dedicated health endpoint. Both startup and liveness probes target / (the Invoice Ninja login or dashboard page), which returns HTTP 200 when the application is fully initialised.
Invoice Ninja performs PHP-FPM initialisation, database migrations, and configuration caching on first boot. The startup probe defaults allow 90 seconds of initial delay plus up to 30 retry periods of 15 seconds each — giving Invoice Ninja up to 540 seconds of total startup tolerance on cold deployments with pending migrations.
| Variable | Group | Default | Description |
|---|---|---|---|
startup_probe | 14 | { enabled=true, type="HTTP", path="/", initial_delay_seconds=90, timeout_seconds=10, period_seconds=15, failure_threshold=30 } | Startup readiness probe. Container receives no traffic until this succeeds. |
liveness_probe | 14 | { enabled=true, type="HTTP", path="/", initial_delay_seconds=120, timeout_seconds=10, period_seconds=30, failure_threshold=3 } | Liveness probe. Container is restarted after failure_threshold consecutive failures. |
uptime_check_config | 14 | { enabled=false, path="/" } | Cloud Monitoring uptime check (disabled by default). When enabled, alerts notify support_users if unreachable. |
alert_policies | 14 | [] | Cloud Monitoring metric alert policies. |
D. Auto Password Rotation
When enable_auto_password_rotation = true, a zero-downtime password rotation pipeline is provisioned:
- Secret Manager emits a rotation notification at every
secret_rotation_periodinterval. - Eventarc fires a Cloud Run rotation Job.
- The job generates a new password, updates the Cloud SQL MySQL user, writes a new secret version.
- After
rotation_propagation_delay_secseconds, the job restarts the Invoice Ninja service.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_auto_password_rotation | 12 | false | Enables automated password rotation. |
rotation_propagation_delay_sec | 12 | 90 | Seconds to wait after writing the new secret before restarting the service. |
8. Integrations
A. Redis Queue & Cache
Redis is required for production Invoice Ninja deployments (enable_redis = true by default). Invoice Ninja uses Redis for three critical functions:
QUEUE_CONNECTION=redis— Background job processing including PDF generation, email delivery, and webhook dispatching. Without Redis, these operations block the HTTP request cycle causing timeouts.CACHE_DRIVER=redis— Application-level caching for settings, company data, and tax calculations.SESSION_DRIVER=redis— Session storage ensuring users remain logged in across multiple Cloud Run instances.
When enable_redis = true and redis_host is not provided, the module defaults to using the NFS server IP as the Redis host (a lightweight Redis instance co-located on the NFS GCE VM). For production deployments, point redis_host at a dedicated Google Cloud Memorystore for Redis instance.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_redis | 21 | true | Enables Redis. Required for background PDF generation and email queuing in production. |
redis_host | 21 | "" | Redis server hostname or IP. Leave blank to use the NFS server IP. |
redis_port | 21 | '6379' | Redis server TCP port (string). |
redis_auth | 21 | "" | Redis AUTH password. Sensitive — never stored in state. |
B. SMTP & Email
Invoice Ninja sends transactional email for invoice delivery to clients, quote approvals, payment confirmations, and recurring invoice notifications. SMTP must be configured before going live.
Invoice Ninja-specific variables (Group 23):
| Variable | Group | Default | Description |
|---|---|---|---|
invoiceninja_admin_email | 23 | 'admin@example.com' | Administrator email address for login and system notifications. |
mail_from_name | 23 | 'Invoice Ninja' | Display name shown as the sender on outgoing emails. |
mail_from_address | 23 | 'ninja@example.com' | Sender email address. Should match your verified sending domain. |
SMTP credentials (host, port, user, password) are passed via environment_variables and secret_environment_variables:
environment_variables = {
MAIL_MAILER = "smtp"
MAIL_HOST = "smtp.mailgun.org"
MAIL_PORT = "587"
MAIL_USERNAME = "postmaster@mg.example.com"
}
secret_environment_variables = {
MAIL_PASSWORD = "invoiceninja-smtp-password"
}
C. PDF Generation (snappdf / Chromium)
Invoice Ninja bundles snappdf with a headless Chromium executable at /usr/local/bin/chrome. The following environment variables are set automatically by InvoiceNinja Common:
PDF_GENERATOR=snappdfSNAPPDF_EXECUTABLE_PATH=/usr/local/bin/chrome
CPU and memory requirements for PDF generation: Chromium spawns a separate process for each PDF render. Under concurrent load (multiple clients viewing invoices simultaneously), each PDF generation can consume up to 500 MB of RAM and 1 vCPU. The default 2 vCPU / 2 Gi allocation supports approximately 2–4 concurrent PDF renders. Increase memory_limit to '4Gi' for high-volume invoice environments.
D. Backup Import & Recovery
When enable_backup_import = true, a dedicated Cloud Run Job restores an existing database backup into the provisioned Cloud SQL MySQL instance during apply.
| Variable | Group | Default | Description |
|---|---|---|---|
backup_schedule | 7 | '0 2 * * *' | Cron expression (UTC) for automated daily backups. |
backup_retention_days | 7 | 7 | Days to retain backup files in GCS. |
enable_backup_import | 7 | false | Triggers a one-time restore on apply. Set false after a successful import. |
backup_source | 7 | 'gcs' | 'gcs' (full GCS URI) or 'gdrive' (Drive file ID). |
backup_uri | 7 | "" | Full GCS URI (e.g., 'gs://my-bucket/invoiceninja-2024-01.sql') or Google Drive file ID. |
backup_format | 7 | 'sql' | Backup file format. Options: sql, tar, gz, tgz, tar.gz, zip. |
E. Observability & Alerting
A Cloud Monitoring uptime check polls the Invoice Ninja endpoint from multiple global locations. Custom alert policies can monitor Cloud Run metrics (latency, error rate, instance count) and notify support_users.
| Variable | Group | Default | Description |
|---|---|---|---|
uptime_check_config | 14 | { enabled=false, path="/" } | Uptime check configuration. Disabled by default. |
alert_policies | 14 | [] | Metric alert policies. |
support_users | 2 | [] | Email addresses notified by uptime and alert policy triggers. |
9. Platform-Managed Behaviours
The following behaviours are applied automatically by InvoiceNinja CloudRun regardless of variable values.
| Behaviour | Implementation | Detail |
|---|---|---|
| MySQL 8.0 required | database_type = "MYSQL_8_0" default | Invoice Ninja's Laravel application exclusively targets MySQL. |
| APP_KEY auto-generated | InvoiceNinja Common generates and injects via secret_environment_variables | A base64-encoded 32-byte random Laravel encryption key is generated once and stored in Secret Manager. It is NOT regenerated on subsequent applies. |
| snappdf PDF generator | PDF_GENERATOR=snappdf and SNAPPDF_EXECUTABLE_PATH=/usr/local/bin/chrome injected automatically | Chromium is bundled in the invoiceninja/invoiceninja:5 container. |
| TRUSTED_PROXIES=* | Injected automatically | Required for Cloud Run's reverse proxy to correctly forward client IP addresses and HTTPS headers to Laravel. |
| DB_CONNECTION=mysql | Injected automatically | Laravel database driver selection. |
| Redis required for queues | enable_redis = true default | Invoice Ninja background jobs (PDF generation, email) fail silently or time out without Redis. |
| Default db-init + artisan-migrate jobs | Supplied by InvoiceNinja Common when initialization_jobs = [] | MySQL database, user, and schema are created automatically. artisan-migrate runs on every apply to handle version upgrades. |
| NFS enabled by default | enable_nfs = true default | Invoice Ninja stores document uploads, client logos, and generated PDFs in the container filesystem. Without NFS, these files are lost on each new revision. |
| min_instance_count = 0 | Default (matches App_CloudRun) | Invoice Ninja scales to zero by default; set min_instance_count = 1 to keep an instance warm and avoid cold-start latency on client invoice delivery. |
10. Exploring with the GCP Console
After deployment, navigate to these locations in the GCP Console to explore the Invoice Ninja infrastructure.
Cloud Run Service
- Navigate to Cloud Run → select the
invoiceninja-<deployment-id>service. - Revisions tab: shows all deployed revisions, traffic allocation, and container image digests. Each
artisan-migraterun creates a new revision. - Logs tab: stream application logs. Filter for
severity=ERRORto find PHP exceptions and failed queue jobs. Look forLaravellog entries and Chromium crash messages. - Metrics tab: view request count, request latency, container instance count, CPU utilisation, and memory utilisation. Watch memory usage during PDF generation — spikes above 1.8 Gi indicate headroom pressure.
- YAML tab: inspect the full Cloud Run service configuration including all environment variables (plaintext only — secrets show as references), volume mounts, and probe configuration.
Cloud Run Jobs
- Navigate to Cloud Run → Jobs. Look for:
invoiceninja-<deployment-id>-db-init— the MySQL database initialisation job.invoiceninja-<deployment-id>-artisan-migrate— the Laravel migration job.
- Select a job and click Executions to see the history of each run, exit codes, and log output.
- Click an individual execution to stream its logs — look for Laravel's migration output confirming tables created or skipped.
Cloud SQL
- Navigate to SQL → select the Cloud SQL MySQL 8.0 instance (named
app<name><deploymentid>or similar). - Overview: shows CPU, memory, and storage usage. Invoice Ninja's query pattern is read-heavy (report generation) — monitor read IOPS.
- Connections tab: confirms active connections from the Cloud Run Auth Proxy. Each Cloud Run instance maintains a pool of connections via the sidecar.
- Databases: verify the
invoiceninjadatabase exists and was created by thedb-initjob. - Users: verify the
invoiceninjauser exists with appropriate privileges. - Backups: view automated backup snapshots and their retention status.
Secret Manager
- Navigate to Security → Secret Manager.
- Find the following secrets (prefix varies by deployment):
app<name><id>-db-password— the Invoice Ninja MySQL user password.app<name><id>-db-root-password— the MySQL root password used bydb-init.app<name><id>-app-key— the LaravelAPP_KEYencryption key.
- Click any secret to view versions, access history, and rotation configuration.
- Versions tab: confirm the latest version is
ENABLED. ADESTROYEDorDISABLEDversion causes Invoice Ninja to fail at startup with a secret access error.
Cloud Storage
- Navigate to Cloud Storage → Buckets.
- Find the
app<name><id>-databucket provisioned for Invoice Ninja document storage. - Browse the bucket contents to see uploaded client logos, invoice attachments, and generated PDF files.
- Check Lifecycle rules to confirm backup retention policies are applied.
Artifact Registry
- Navigate to Artifact Registry → Repositories.
- Find the repository for this deployment. The
invoiceninja/invoiceninja:5image (or a custom build) is stored here after mirroring. - View image tags, digest values, and vulnerability scan results.
- Check that the cleanup policy is removing untagged images and images beyond
max_images_to_retain.
Monitoring
- Navigate to Monitoring → Uptime checks to view the Invoice Ninja endpoint uptime status from global probe locations.
- Navigate to Monitoring → Alerting to view configured alert policies for this deployment. Alerts fire to
support_userswhen the uptime check fails. - Navigate to Monitoring → Dashboards → select the Cloud Run dashboard for per-service metrics.
11. Exploring with gcloud
Use these commands to inspect and troubleshoot the Invoice Ninja deployment from the command line. Replace PROJECT_ID, REGION, SERVICE_NAME, and DEPLOYMENT_ID with your actual values.
Inspect the Cloud Run service
# List all Cloud Run services in the project
gcloud run services list \
--project=PROJECT_ID \
--region=REGION \
--format="table(metadata.name,status.url,status.conditions[0].status)"
# Describe the Invoice Ninja service in full detail
gcloud run services describe invoiceninja-DEPLOYMENT_ID \
--project=PROJECT_ID \
--region=REGION \
--format=yaml
# Show the current revision serving traffic
gcloud run revisions list \
--service=invoiceninja-DEPLOYMENT_ID \
--project=PROJECT_ID \
--region=REGION \
--format="table(metadata.name,status.conditions[0].type,spec.containers[0].image)"
Stream and filter Cloud Run logs
# Tail Invoice Ninja application logs
gcloud logging read \
'resource.type="cloud_run_revision" AND resource.labels.service_name="invoiceninja-DEPLOYMENT_ID"' \
--project=PROJECT_ID \
--limit=100 \
--format="table(timestamp,severity,textPayload)" \
--freshness=1h
# Filter for errors only
gcloud logging read \
'resource.type="cloud_run_revision" AND resource.labels.service_name="invoiceninja-DEPLOYMENT_ID" AND severity>=ERROR' \
--project=PROJECT_ID \
--limit=50 \
--format="table(timestamp,textPayload)"
# Watch for Chromium/PDF generation events
gcloud logging read \
'resource.type="cloud_run_revision" AND resource.labels.service_name="invoiceninja-DEPLOYMENT_ID" AND textPayload=~"pdf|chromium|snappdf"' \
--project=PROJECT_ID \
--limit=20 \
--format="table(timestamp,textPayload)"
Inspect Cloud Run Jobs (db-init, artisan-migrate)
# List all Cloud Run jobs
gcloud run jobs list \
--project=PROJECT_ID \
--region=REGION \
--format="table(metadata.name,status.conditions[0].type)"
# View recent executions of the artisan-migrate job
gcloud run jobs executions list \
--job=invoiceninja-DEPLOYMENT_ID-artisan-migrate \
--project=PROJECT_ID \
--region=REGION \
--format="table(metadata.name,status.conditions[0].type,status.conditions[0].lastTransitionTime)"
# Stream logs from the most recent artisan-migrate execution
gcloud logging read \
'resource.type="cloud_run_job" AND resource.labels.job_name="invoiceninja-DEPLOYMENT_ID-artisan-migrate"' \
--project=PROJECT_ID \
--limit=100 \
--format="table(timestamp,textPayload)" \
--freshness=1d
Inspect Cloud SQL
# List all Cloud SQL instances in the project
gcloud sql instances list \
--project=PROJECT_ID \
--format="table(name,databaseVersion,state,ipAddresses[0].ipAddress)"
# Describe the Invoice Ninja MySQL instance
gcloud sql instances describe INSTANCE_NAME \
--project=PROJECT_ID \
--format="yaml(name,databaseVersion,state,settings.tier,settings.dataDiskSizeGb,settings.ipConfiguration)"
# List databases on the instance
gcloud sql databases list \
--instance=INSTANCE_NAME \
--project=PROJECT_ID
# List Cloud SQL users
gcloud sql users list \
--instance=INSTANCE_NAME \
--project=PROJECT_ID \
--format="table(name,host,passwordPolicy.status)"
# Check current active connections
gcloud sql operations list \
--instance=INSTANCE_NAME \
--project=PROJECT_ID \
--limit=10 \
--format="table(name,operationType,status,startTime)"
Inspect Secret Manager
# List all secrets for this deployment
gcloud secrets list \
--project=PROJECT_ID \
--filter="name~invoiceninja" \
--format="table(name,replication.automatic,createTime)"
# Check the APP_KEY secret versions
gcloud secrets versions list app-key-SECRET_NAME \
--project=PROJECT_ID \
--format="table(name,state,createTime)"
# Access the APP_KEY value (requires secretAccessor role — use with care)
gcloud secrets versions access latest \
--secret=APP_KEY_SECRET_NAME \
--project=PROJECT_ID
Inspect GCS storage
# List storage buckets for this deployment
gcloud storage ls --project=PROJECT_ID | grep invoiceninja
# List top-level objects in the data bucket
gcloud storage ls gs://BUCKET_NAME/
# Check bucket IAM bindings
gcloud storage buckets get-iam-policy gs://BUCKET_NAME
# View lifecycle rules
gcloud storage buckets describe gs://BUCKET_NAME \
--format="json(lifecycle)"
Inspect Artifact Registry images
# List images in the repository
gcloud artifacts docker images list \
REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/invoiceninja \
--project=PROJECT_ID \
--format="table(image,tags,createTime)"
# Show vulnerability scan results
gcloud artifacts docker images scan \
REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/invoiceninja:5 \
--project=PROJECT_ID \
--format="json"
Check Memorystore Redis (if using a dedicated instance)
# List Redis instances
gcloud redis instances list \
--region=REGION \
--project=PROJECT_ID \
--format="table(name,host,port,state,memorySizeGb,authEnabled)"
# Describe a specific Redis instance
gcloud redis instances describe REDIS_INSTANCE_NAME \
--region=REGION \
--project=PROJECT_ID
Uptime checks and alerting
# List uptime checks for this project
gcloud monitoring uptime list-configs \
--project=PROJECT_ID \
--format="table(displayName,monitoredResource.labels.host,period,timeout)"
# List alert policies
gcloud alpha monitoring policies list \
--project=PROJECT_ID \
--format="table(displayName,enabled,conditions[0].conditionThreshold.filter)"
12. Variable Reference
All user-configurable variables exposed by InvoiceNinja CloudRun, sorted by UI group then order. | Variable | Group | Default | Description |
|---|---|---|---|
| project_id | 1 | — | GCP project ID. Required. |
| region | 1 | 'us-central1' | GCP region for resource deployment. |
| tenant_deployment_id | 2 | 'demo' | Short suffix appended to all resource names. |
| support_users | 2 | [] | Email addresses for monitoring alerts. |
| resource_labels | 2 | {} | Labels applied to all provisioned resources. |
| application_name | 3 | 'invoiceninja' | Base resource name. Do not change after initial deployment. |
| application_display_name | 3 | 'Invoice Ninja' | Human-readable name shown in the GCP Console. |
| application_description | 3 | 'Invoice Ninja - Open-source invoicing platform' | Service description. |
| application_version | 3 | '5' | Invoice Ninja container image tag. |
| deploy_application | 4 | true | Set false for infrastructure-only deployment. |
| container_image_source | 4 | 'prebuilt' | 'prebuilt' (official image) or 'custom' (Cloud Build). |
| container_image | 4 | "" | Override image URI. Leave empty for the default Invoice Ninja image. |
| min_instance_count | 4 | 0 | Minimum instances (scale-to-zero by default). Set 1 to keep Invoice Ninja warm. |
| max_instance_count | 4 | 3 | Maximum instances. Acts as cost ceiling. |
| cpu_limit | 4 | '2000m' | CPU per instance. 2 vCPU minimum for PDF generation. |
| memory_limit | 4 | '2Gi' | Memory per instance. 2 Gi minimum for Chromium. |
| container_port | 4 | 80 | Invoice Ninja's nginx port. |
| execution_environment | 4 | 'gen2' | Gen2 required for NFS mounts and GCS Fuse. |
| timeout_seconds | 4 | 300 | Max request duration. Increase for PDF generation. |
| enable_cloudsql_volume | 4 | true | Mounts Cloud SQL Auth Proxy Unix socket. |
| cpu_always_allocated | 4 | false | Request-based billing by default. See §3.A for the queue/scheduler trade-off. |
| cloudsql_volume_mount_path | 4 | '/cloudsql' | Container path for the Auth Proxy Unix socket. |
| container_protocol | 4 | 'http1' | 'http1' or 'h2c'. |
| enable_image_mirroring | 4 | true | Mirrors the Invoice Ninja image into Artifact Registry. |
| traffic_split | 4 | [] | Canary/blue-green traffic allocation. |
| max_revisions_to_retain | 4 | 7 | Maximum Cloud Run revisions to keep. |
| service_annotations | 4 | {} | Advanced Cloud Run annotations. |
| service_labels | 4 | {} | Labels applied to the Cloud Run service. |
| ingress_settings | 5 | 'all' | 'all', 'internal', or 'internal-and-cloud-load-balancing'. |
| vpc_egress_setting | 5 | 'PRIVATE_RANGES_ONLY' | 'PRIVATE_RANGES_ONLY' or 'ALL_TRAFFIC'. |
| enable_iap | 5 | false | Enables IAP natively on the Cloud Run service. |
| iap_authorized_users | 5 | [] | Users/SAs granted IAP access. |
| iap_authorized_groups | 5 | [] | Google Groups granted IAP access. |
| environment_variables | 6 | {} | Plain-text env vars. Use for SMTP host, port, username, and Mail configuration. |
| secret_environment_variables | 6 | {} | Secret Manager references (e.g., { MAIL_PASSWORD = "smtp-password-secret" }). |
| explicit_secret_values | 6 | {} | Raw sensitive values written directly into Secret Manager during deployment. |
| secret_propagation_delay | 6 | 30 | Seconds to wait after secret creation. |
| secret_rotation_period | 6 | '2592000s' | Secret Manager rotation notification frequency. |
| backup_schedule | 7 | '0 2 * * *' | Cron expression (UTC) for automated backups. |
| backup_retention_days | 7 | 7 | Days to retain backup files in GCS. |
| enable_backup_import | 7 | false | Triggers a one-time restore on apply. |
| backup_source | 7 | 'gcs' | 'gcs' (full URI) or 'gdrive' (file ID). |
| backup_uri | 7 | "" | Full GCS URI or Google Drive file ID. |
| backup_format | 7 | 'sql' | Backup format. Options: sql, tar, gz, tgz, tar.gz, zip. |
| enable_cicd_trigger | 8 | false | Provisions a Cloud Build GitHub trigger. |
| github_repository_url | 8 | "" | Full HTTPS URL of the GitHub repository. |
| github_token | 8 | "" | GitHub PAT. Required on first apply. Sensitive. |
| github_app_installation_id | 8 | "" | GitHub App installation ID. |
| cicd_trigger_config | 8 | { branch_pattern = "^main$" } | Advanced Cloud Build trigger config. |
| enable_cloud_deploy | 8 | false | Provisions a Cloud Deploy progressive delivery pipeline. |
| cloud_deploy_stages | 8 | [dev, staging, prod(approval)] | Ordered Cloud Deploy promotion stages. |
| enable_binary_authorization | 8 | false | Enforces image attestation on deployment. |
| enable_custom_sql_scripts | 9 | false | Runs SQL scripts from GCS after provisioning. |
| custom_sql_scripts_bucket | 9 | "" | GCS bucket containing SQL scripts. |
| custom_sql_scripts_path | 9 | "" | Path prefix within the bucket. |
| custom_sql_scripts_use_root | 9 | false | Run scripts as the root DB user. |
| enable_cloud_armor | 10 | false | Provisions Global HTTPS LB + Cloud Armor WAF. |
| admin_ip_ranges | 10 | [] | CIDR ranges exempted from WAF rules. |
| application_domains | 10 | [] | Custom domains with Google-managed SSL certificates. |
| enable_cdn | 10 | false | Enables Cloud CDN on the HTTPS LB backend. |
| max_images_to_retain | 10 | 7 | Maximum recent container images to keep in Artifact Registry. |
| delete_untagged_images | 10 | true | Automatically deletes untagged images from Artifact Registry. |
| image_retention_days | 10 | 30 | Days after which images are eligible for deletion. |
| create_cloud_storage | 11 | true | Set false to skip GCS bucket creation. |
| storage_buckets | 11 | [{ name_suffix = "data" }] | GCS buckets to provision. |
| enable_nfs | 11 | true | Provisions NFS shared storage. Requires gen2. |
| nfs_mount_path | 11 | '/mnt/nfs' | Container path where NFS is mounted. |
| nfs_instance_name | 9 | "" | Name of an existing NFS GCE VM. Leave empty to auto-discover. |
| nfs_instance_base_name | 9 | 'app-nfs' | Base name for inline NFS VM. Deployment ID is appended. |
| gcs_volumes | 11 | [] | GCS buckets to mount via GCS Fuse. |
| manage_storage_kms_iam | 11 | false | Creates CMEK KMS key and enables CMEK on storage buckets. |
| enable_artifact_registry_cmek | 11 | false | Creates Artifact Registry KMS key for at-rest image encryption. |
| database_type | 12 | 'MYSQL_8_0' | Cloud SQL engine. Must remain MYSQL_8_0 for Invoice Ninja. |
| application_database_name | 12 | 'invoiceninja' | MySQL database name. Do not change after initial deployment. |
| application_database_user | 12 | 'invoiceninja' | MySQL application user. |
| database_password_length | 12 | 32 | Auto-generated password length. Range: 16–64. |
| enable_auto_password_rotation | 12 | false | Automated zero-downtime password rotation. |
| rotation_propagation_delay_sec | 12 | 90 | Seconds to wait after rotation before restarting the service. |
| initialization_jobs | 13 | [] | One-shot Cloud Run Jobs. Leave empty for default db-init and artisan-migrate. |
| cron_jobs | 13 | [] | Recurring scheduled Cloud Run Jobs. |
| startup_probe | 14 | { path="/", initial_delay_seconds=90, failure_threshold=30, ... } | Startup probe. Long initial delay for Invoice Ninja PHP initialisation. |
| liveness_probe | 14 | { path="/", initial_delay_seconds=120, failure_threshold=3, ... } | Liveness probe. |
| uptime_check_config | 14 | { enabled=false, path="/" } | Cloud Monitoring uptime check (disabled by default). |
| alert_policies | 14 | [] | Cloud Monitoring metric alert policies. |
| enable_redis | 21 | true | Enabled by default. Required for PDF generation and email queuing. |
| redis_host | 21 | "" | Redis hostname/IP. Defaults to NFS server IP when empty. |
| redis_port | 21 | '6379' | Redis TCP port (string). |
| redis_auth | 21 | "" | Redis AUTH password. Sensitive. |
| enable_vpc_sc | 22 | false | Registers API calls within the project's VPC-SC perimeter. |
| vpc_cidr_ranges | 22 | [] | VPC subnet CIDR ranges for VPC-SC access level. |
| vpc_sc_dry_run | 22 | true | Logs VPC-SC violations without blocking. Set false to enforce. |
| organization_id | 22 | "" | GCP Organization ID for VPC-SC. Auto-discovered when empty. |
| enable_audit_logging | 22 | false | Enables detailed Cloud Audit Logs. |
| invoiceninja_admin_email | 23 | 'admin@example.com' | Administrator email for login and notifications. |
| mail_from_name | 23 | 'Invoice Ninja' | Display name for outgoing emails. |
| mail_from_address | 23 | 'ninja@example.com' | Sender email address for outgoing emails. |
13. Outputs
| Output | Description |
|---|---|
service_name | Name of the Cloud Run service. |
service_url | Public URL of the Cloud Run service. |
service_location | GCP region where the Cloud Run service is deployed. |
project_id | GCP project ID. |
deployment_id | Deployment ID suffix used in resource names. |
database_instance_name | Name of the Cloud SQL MySQL instance. |
database_name | Name of the application database. |
database_user | Name of the application database user. |
database_password_secret | Secret Manager secret name for the database password. |
storage_buckets | Created GCS storage buckets. |
container_image | Container image used for the deployment. |
cicd_enabled | Whether the CI/CD pipeline is enabled. |
github_repository_url | GitHub repository URL connected for CI/CD. |
14. Configuration Pitfalls & Sensible Defaults
Risk levels: Critical (data loss, full outage, security breach) — High (service unavailable or significant degradation) — Medium (degraded function or increased cost) — Low (minor impact).
| Variable | Sensible Default | Risk | Consequence of Incorrect Value |
|---|---|---|---|
project_id | (required) | Critical | No default — deployment fails immediately. |
database_type | "MYSQL_8_0" | Critical | Invoice Ninja's Laravel application exclusively uses MySQL. Setting to POSTGRES causes Invoice Ninja to fail at startup with a PDO driver error. |
application_database_name | "invoiceninja" | Critical | Immutable after first deployment. Changing this causes Terraform to recreate the database, destroying all invoice, client, and payment data. |
application_database_user | "invoiceninja" | Critical | Immutable after first deployment. Changing this recreates the MySQL user, invalidating stored credentials and breaking the application. |
memory_limit | "2Gi" | High | Invoice Ninja spawns a Chromium process for each PDF render. Under 1 Gi, Chromium OOM-kills during PDF generation, returning blank PDFs or 500 errors to clients. Minimum for Chromium is approximately 512 Mi per concurrent render. |
cpu_limit | "2000m" | High | Below 1 vCPU, Chromium PDF rendering slows dramatically and may time out. timeout_seconds may need to be increased alongside any CPU reduction. |
enable_redis | true | Critical | Invoice Ninja REQUIRES Redis for background queue processing. Without Redis, invoice PDF generation and email delivery are synchronous — they block the HTTP request, cause timeouts, and fail under concurrent load. Disabling Redis in production makes the invoice delivery workflow unreliable. |
redis_host | "" (auto-resolves to NFS IP) | High | If enable_nfs = false and redis_host remains empty with enable_redis = true, Invoice Ninja cannot connect to Redis at startup. The application will start but queue jobs will fail silently. |
enable_nfs | true | High | Invoice Ninja writes uploaded documents, client logos, and cached data to the container filesystem. Without NFS, files uploaded on one instance are invisible to others, and all user uploads are lost on each new Cloud Run revision deployment. |
min_instance_count | 0 (default) — set 1 for production | Medium | The default scale-to-zero causes cold starts. Invoice Ninja's PHP-FPM initialisation + database migration check takes 10–30 seconds. Clients accessing invoice links during cold start receive timeout errors. |
container_image_source | "prebuilt" | Low | The official invoiceninja/invoiceninja image is production-ready. Switching to "custom" requires a valid Dockerfile in InvoiceNinja_Common/scripts/ — if the build fails, no new revision is deployed. |
invoiceninja_admin_email | "admin@example.com" | Medium | The admin email must be a valid, accessible address. Using the default placeholder prevents the administrator from receiving system notifications and blocks initial password reset flows. |
mail_from_address | "ninja@example.com" | High | Invoice delivery emails sent from an unverified domain are likely rejected by client mail servers or marked as spam. Configure a verified sender domain before sending invoices to clients. |
backup_retention_days | 7 | Medium | Seven days is insufficient for billing data. Invoice and payment records are legally required to be retained in many jurisdictions. Increase to 90+ days for production deployments. |
enable_cloud_armor | false | Medium | Without Cloud Armor, the Invoice Ninja admin panel and client portal are exposed to bot traffic. Invoice Ninja admin credentials can be targeted by credential stuffing attacks. |
timeout_seconds | 300 | Medium | PDF generation for complex multi-line invoices with custom branding can take 10–30 seconds under load. Report exports over large date ranges may exceed 300 seconds. Increase to 600 for high-volume deployments. |
vpc_egress_setting | "PRIVATE_RANGES_ONLY" | Medium | Invoice Ninja needs to reach external SMTP servers for email delivery. PRIVATE_RANGES_ONLY permits direct public egress for SMTP. Setting "ALL_TRAFFIC" with a restrictive VPC firewall blocks outbound SMTP. |
15. 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.
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.