Skip to main content

Certification track: Associate Cloud Engineer (ACE)

Paperless-ngx on Google Cloud Run

Paperless-ngx on Google Cloud Run

This document provides a comprehensive reference for the modules/Paperless_CloudRun module. It covers architecture, IAM, configuration variables, Paperless-ngx-specific behaviours, and operational patterns for deploying Paperless-ngx on Google Cloud Run (v2).


1. Module Overview

Paperless-ngx is a community-supported open-source document management system that transforms paper documents into a searchable digital archive. Paperless CloudRun is a wrapper module built on top of App CloudRun. It uses App CloudRun for all GCP infrastructure provisioning and injects Paperless-ngx-specific application configuration, database initialisation, and storage configuration via Paperless Common.

Key Capabilities:

  • Compute: Cloud Run v2 (Gen2), Python/gunicorn container, 2 vCPU / 2 Gi by default. min_instance_count = 0 by default (scale-to-zero) — set to 1 or more to keep the background consumption pipeline warm and avoid cold-start delays on document uploads.
  • OCR & Classification: Tesseract OCR engine with configurable language packs (ocr_language). Machine learning document classification, full-text search, tags, correspondents, document types, and custom fields.
  • Data Persistence: Cloud SQL PostgreSQL 15. GCS FUSE volume auto-mounted at /usr/src/paperless/media for persistent document storage (processed documents, thumbnails, originals).
  • Task Queue: Redis required (not optional). Paperless-ngx uses Redis as a Celery message broker for background OCR processing, document classification, consumption pipeline, and async tasks.
  • Security: Inherits Cloud Armor WAF, IAP, Binary Authorization, and VPC Service Controls from App CloudRun. Two secrets auto-generated by Paperless Common: PAPERLESS_ADMIN_PASSWORD and PAPERLESS_SECRET_KEY.
  • CI/CD: Cloud Build custom image pipeline by default; Cloud Deploy progressive delivery optional.
  • Reliability: Health probes target / (the Paperless-ngx login page) with a 60-second initial delay to accommodate database migrations and Celery worker startup on first boot.

Project & Application Identity

VariableGroupTypeDefaultDescription
project_id1stringGCP project ID. Required.
tenant_deployment_id2string'demo'Short suffix appended to all resource names.
support_users2list(string)[]Email recipients for monitoring alerts.
resource_labels2map(string){}Labels applied to all provisioned resources.
application_name3string'paperless'Base resource name. Do not change after initial deployment.
display_name3string'Paperless-ngx - Document Management System'Human-readable name shown in dashboards.
description3string'Paperless-ngx - open-source document management system with OCR...'Service description.
application_version3string'latest'Container image version tag. Pin to a specific version (e.g., '2.13.5') for production.

Wrapper architecture: Paperless CloudRun calls Paperless Common to build an application_config object containing Paperless-ngx-specific environment variables, probe configuration, storage bucket definitions, and the db-init job definition. module_storage_buckets carries the paperless-media bucket provisioned by Paperless Common. scripts_dir is resolved to the Paperless Common scripts directory at apply time.


2. IAM & Access Control

Paperless_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 secrets: Unlike Ghost, Paperless Common auto-generates two application-level secrets at deploy time:

SecretDescription
PAPERLESS_ADMIN_PASSWORDPassword for the initial admin account. Auto-generated and stored in Secret Manager.
PAPERLESS_SECRET_KEYDjango application secret key. Auto-generated and stored in Secret Manager.

Both secrets are injected natively by Cloud Run at revision start — the plaintext is never written to Terraform state.

Database initialisation identity: The db-init Cloud Run Job runs under the Cloud Run SA. It connects to Cloud SQL PostgreSQL via the Auth Proxy Unix socket (since enable_cloudsql_volume = true by default).

120-second IAM propagation delay: Inherited from App CloudRun — the Paperless-ngx service is not deployed until the delay completes, preventing secret-read failures on the first revision start.

For the complete role tables and IAP, password rotation, and public access details, see the App_CloudRun documentation.


3. Core Service Configuration

A. Compute (Cloud Run)

Paperless-ngx is a Python Django/gunicorn application with background Celery workers. It performs OCR using Tesseract, which is CPU-intensive on document ingestion. Paperless CloudRun exposes cpu_limit and memory_limit as dedicated top-level variables with production-ready defaults.

min_instance_count = 0 by default. Like most modules in this repository, Paperless-ngx scales to zero by default (cost-first). The trade-off: the background consumption pipeline only runs while an instance is warm, so documents dropped into the consumption directory may sit unprocessed until the next request wakes the service. Set min_instance_count = 1 or more to keep the consumption pipeline continuously listening and avoid this delay.

Startup CPU Boost is always enabled (hardcoded in App CloudRun).

Container image: container_image_source defaults to 'custom', meaning Cloud Build compiles a custom image using Paperless_Common's Dockerfile (based on ghcr.io/paperless-ngx/paperless-ngx). Set container_image_source = 'prebuilt' and container_image = 'ghcr.io/paperless-ngx/paperless-ngx:2.13.5' to deploy an existing image directly.

Image mirroring enabled by default (enable_image_mirroring = true): Paperless-ngx images are hosted on GitHub Container Registry (GHCR). The module mirrors the image to Artifact Registry to avoid GHCR rate limits and to satisfy Binary Authorization requirements.

VariableGroupDefaultDescription
deploy_application4trueSet false for infrastructure-only deployment (SQL, storage, secrets).
container_image_source'custom''custom' builds via Cloud Build. 'prebuilt' deploys an existing image URI.
container_image""Override image URI. Leave empty for Cloud Build to manage.
cpu_limit4'2000m'CPU per instance. 2 vCPU recommended for OCR workloads.
memory_limit4'2Gi'Memory per instance. 2 Gi minimum; increase for large document batches.
min_instance_count40Minimum running instances. Scale-to-zero by default; set to 1+ to keep the consumption pipeline active.
max_instance_count43Maximum running instances. Cost ceiling.
container_port48000Paperless-ngx gunicorn port. Do not change without matching the Dockerfile.
execution_environment4'gen2'Gen2 required for GCS Fuse volume mounts.
timeout_seconds4300Max request duration. OCR on large documents can be slow; 300s or higher recommended.
enable_cloudsql_volume4trueDefault true — connects via Cloud SQL Auth Proxy Unix socket.
enable_image_mirroring4trueMirrors the GHCR image into Artifact Registry. Recommended to avoid rate limits.
container_protocol4'http1''http1' or 'h2c'.
traffic_split4[]Percentage-based canary/blue-green traffic allocation.
max_revisions_to_retain47Maximum Cloud Run revisions to keep. Set 0 to disable.
service_annotations4{}Advanced Cloud Run annotations.
service_labels4{}Labels applied to the Cloud Run service.

Differences from App CloudRun defaults:

VariableApp CloudRunPaperless CloudRunReason
container_port80808000Paperless-ngx gunicorn listens on 8000.
cpu_limit'1000m''2000m'OCR with Tesseract is CPU-intensive; 2 vCPU recommended.
memory_limit'512Mi''2Gi'Paperless-ngx loads document thumbnails and ML models in memory.
enable_image_mirroringfalsetrueMirrors from GHCR to avoid rate limits and support Binary Authorization.

B. Database (Cloud SQL — PostgreSQL 15)

Paperless-ngx requires PostgreSQL. Paperless Common fixes database_type = "POSTGRES_15" and configures the connection variables accordingly. MySQL and SQLite are not supported.

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.

VariableGroupDefaultDescription
database_type12'POSTGRES_15'PostgreSQL 15. Do not change — Paperless-ngx requires PostgreSQL.
db_name12'paperless'PostgreSQL database name. Do not change after initial deployment.
db_user12'paperless'PostgreSQL application user. Password auto-generated and stored in Secret Manager.
database_password_length1232Auto-generated password length. Range: 16–64.
enable_auto_password_rotation12falseAutomated zero-downtime password rotation.
rotation_propagation_delay_sec1290Seconds to wait after rotation before restarting the service.

C. Storage (GCS Fuse)

GCS Fuse is the primary persistent storage mechanism. Unlike Ghost (which relies on NFS for content), Paperless-ngx stores all processed documents, thumbnails, and originals in a GCS FUSE-mounted bucket at /usr/src/paperless/media. This design makes Paperless-ngx well-suited for Cloud Run — documents survive instance recycling and scale-to-zero because they are in GCS, not on local disk.

Default GCS Fuse volume: When gcs_volumes = [] (the default), Paperless Common auto-provisions a paperless-media GCS bucket and mounts it at /usr/src/paperless/media with implicit-dirs, stat-cache-ttl=60s, and type-cache-ttl=60s options. GCS Fuse requires execution_environment = 'gen2'.

NFS is also enabled by default (enable_nfs = true). The NFS server IP is used as the Redis host when no redis_host is explicitly configured. NFS also provides a local-filesystem fallback for the consumption directory when needed. Requires execution_environment = 'gen2'.

VariableGroupDefaultDescription
gcs_volumes11[]GCS buckets to mount via GCS Fuse. When empty, Paperless Common auto-mounts the paperless-media bucket at /usr/src/paperless/media. Requires gen2.
create_cloud_storage11trueSet false to skip additional GCS bucket creation.
storage_buckets11[]Additional GCS buckets beyond the auto-provisioned media bucket.
enable_nfs11trueProvisions NFS server. Used as the Redis host when redis_host is blank. Requires gen2.
nfs_mount_path11'/mnt/nfs'Container path for the NFS mount.
nfs_instance_name11""Name of an existing NFS GCE VM. Leave empty to auto-discover or provision inline.
nfs_instance_base_name11'app-nfs'Base name for an inline NFS GCE VM. Deployment ID is appended.
manage_storage_kms_iam11falseCreates a CMEK KMS keyring and enables CMEK on all storage buckets.
enable_artifact_registry_cmek11falseCreates an Artifact Registry KMS key for at-rest image encryption.

D. Networking

VariableGroupDefaultDescription
ingress_settings5'all''all' — public internet; 'internal' — VPC only; 'internal-and-cloud-load-balancing' — forces traffic through the HTTPS Load Balancer.
vpc_egress_setting5'PRIVATE_RANGES_ONLY''PRIVATE_RANGES_ONLY' routes only RFC 1918 traffic via VPC. 'ALL_TRAFFIC' routes all egress via VPC.

E. Initialization & Bootstrap

A db-init Cloud Run Job is automatically provisioned by Paperless Common when initialization_jobs is left as the default empty list ([]). It uses a PostgreSQL client image and executes Paperless_Common/scripts/db-init.sh, which performs the following idempotent operations:

  1. Connects to Cloud SQL PostgreSQL via the Auth Proxy Unix socket.
  2. Creates the paperless database user with the password from Secret Manager.
  3. Creates the paperless database if it does not exist.
  4. Grants the application user full privileges on the database.

Override initialization_jobs with a non-empty list to replace this default with custom jobs. When initialization_jobs is non-empty, Paperless Common does not inject the default db-init job.

Additional recurring cron jobs and additional sidecar Cloud Run services (e.g., Gotenberg for office document conversion, Tika for content extraction) can be configured via cron_jobs and additional_services:

VariableGroupDefaultDescription
initialization_jobs13[]One-shot Cloud Run Jobs. Leave empty for Paperless Common to supply the default db-init job.
cron_jobs13[]Recurring jobs triggered by Cloud Scheduler.
additional_services13[]Additional Cloud Run services deployed alongside Paperless-ngx. Use to add Gotenberg or Tika.

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. Document management systems containing sensitive files are strong candidates for WAF protection.

VariableGroupDefaultDescription
enable_cloud_armor10falseProvisions Global HTTPS LB + Cloud Armor WAF. Required for custom domains, CDN, and DDoS protection.
admin_ip_ranges10[]CIDR ranges exempted from WAF rules (e.g., office VPN).

B. Identity-Aware Proxy (IAP)

When enable_iap = true, Cloud Run's native IAP integration is enabled directly on the service. Google identity authentication is required before requests reach Paperless-ngx. Recommended for internal document archives where access should be restricted to authenticated organisational users.

VariableGroupDefaultDescription
enable_iap5falseEnables IAP natively on the Cloud Run service.
iap_authorized_users5[]Users/service accounts granted access. Format: 'user:email' or 'serviceAccount:sa@...'.
iap_authorized_groups5[]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.

VariableGroupDefaultDescription
enable_binary_authorization8falseEnforces 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.

VariableGroupDefaultDescription
enable_vpc_sc22falseRegisters module API calls within the project's VPC-SC perimeter. A perimeter must already exist before enabling.

E. Secret Manager Integration

Paperless-ngx application secrets are stored in Secret Manager and injected natively by Cloud Run at revision start — plaintext is never written to Terraform state.

Paperless Common auto-generates PAPERLESS_ADMIN_PASSWORD and PAPERLESS_SECRET_KEY. User-defined secrets can be added via secret_environment_variables.

VariableGroupDefaultDescription
secret_environment_variables6{}Map of env var name → Secret Manager secret ID. Resolved at runtime.
secret_rotation_period6'2592000s'Frequency at which Secret Manager emits rotation notifications. Default: 30 days.
secret_propagation_delay630Seconds 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 Paperless-ngx 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.

Paperless-ngx consideration: Paperless-ngx serves mostly authenticated, user-specific content (document lists, previews, download links). CDN caching is most beneficial for static assets (CSS, JS, thumbnails). Ensure authenticated document responses include Cache-Control: private headers before enabling CDN to prevent cross-user cache poisoning.

VariableGroupDefaultDescription
enable_cdn10falseEnables Cloud CDN on the HTTPS LB backend. Only effective when enable_cloud_armor = true.
max_images_to_retain107Maximum number of recent container images to keep in Artifact Registry. Set 0 to disable.
delete_untagged_images10trueAutomatically deletes untagged images from Artifact Registry.
image_retention_days1030Days after which images are eligible for deletion.

C. Custom Domains

VariableGroupDefaultDescription
application_domains10[]Custom domain names for the HTTPS LB. Google-managed SSL certificates provisioned per domain.

After the first apply, retrieve the LB IP from the Terraform output load_balancer_ip and create an A record. SSL certificate provisioning takes 10–30 minutes after DNS propagation.


6. CI/CD & Delivery

A. Cloud Build Triggers

When enable_cicd_trigger = true, a Cloud Build GitHub connection and push trigger are provisioned. The trigger builds and deploys a custom Paperless-ngx image when code is pushed to the configured branch.

VariableGroupDefaultDescription
enable_cicd_trigger8falseProvisions a Cloud Build GitHub trigger.
github_repository_url8""Full HTTPS URL of the GitHub repository.
github_token8""GitHub PAT. Required on first apply. Sensitive.
github_app_installation_id8""GitHub App installation ID (preferred for organisation repos).
cicd_trigger_config8{ 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.

VariableGroupDefaultDescription
enable_cloud_deploy8falseProvisions a Cloud Deploy pipeline. Requires enable_cicd_trigger = true.
cloud_deploy_stages8[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 and max_instance_count = 3 are configurable via variables (unlike Ghost's hardcoded values). Paperless-ngx OCR tasks run asynchronously via Celery workers backed by Redis — the web UI remains responsive during heavy OCR jobs. Multiple Cloud Run instances can run concurrently with documents stored in GCS.

Consumption pipeline note: The background document consumption pipeline requires at least one running instance. Setting min_instance_count = 0 means uploaded documents will not be processed until the next request warms the instance.

B. Traffic Splitting

VariableGroupDefaultDescription
traffic_split4[]Percentage-based traffic allocation across named revisions. All entries must sum to 100. Empty sends 100% to the latest revision.

C. Health Probes & Uptime Monitoring

Both startup and liveness probes target / (Paperless-ngx's login page), which returns HTTP 200 when the application is fully initialised. Paperless-ngx performs database migrations and starts Celery workers on first boot — the startup probe allows 60 seconds of initial delay with 60 failure thresholds before giving up.

VariableGroupDefaultDescription
startup_probe14{ enabled=true, type="HTTP", path="/", initial_delay_seconds=60, timeout_seconds=10, period_seconds=10, failure_threshold=60 }Startup probe. 60 × 10s = 600s total allowance for first boot.
liveness_probe14{ enabled=true, type="HTTP", path="/", initial_delay_seconds=60, timeout_seconds=10, period_seconds=30, failure_threshold=3 }Liveness probe. Container is restarted after 3 consecutive failures.
uptime_check_config14{ enabled=false, path="/" }Optional Cloud Monitoring uptime check. Alerts notify support_users if unreachable.
alert_policies14[]Cloud Monitoring metric alert policies.

D. Auto Password Rotation

When enable_auto_password_rotation = true, a zero-downtime password rotation pipeline is provisioned:

  1. Secret Manager emits a rotation notification at every secret_rotation_period interval.
  2. Eventarc fires a Cloud Run rotation Job.
  3. The job generates a new password, updates the Cloud SQL PostgreSQL user, and writes a new secret version.
  4. After rotation_propagation_delay_sec seconds, the job restarts the Paperless-ngx service.
VariableGroupDefaultDescription
enable_auto_password_rotation12falseEnables automated password rotation.
rotation_propagation_delay_sec1290Seconds to wait after writing the new secret before restarting.

8. Integrations

A. Redis (Required)

Redis is required and enabled by default (enable_redis = true). Paperless-ngx uses Redis as its Celery message broker — background OCR processing, document classification, consumption pipeline tasks, and asynchronous API calls all depend on Redis. Paperless-ngx will fail to start without a reachable Redis instance.

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.

VariableGroupDefaultDescription
enable_redis21trueRequired. Redis for Celery task queue and result backend.
redis_host21""Redis server hostname or IP. Defaults to NFS server IP when empty. Override with Memorystore for production.
redis_port21'6379'Redis server TCP port (string).
redis_auth21""Redis AUTH password. Leave empty if Redis does not require authentication. Sensitive.

B. Paperless-ngx Application Settings

Paperless-ngx exposes several application-level settings as first-class variables. These are injected as environment variables by Paperless Common.

VariableGroupDefaultDescription
time_zone15'UTC'Timezone for document timestamps and scheduled tasks. Important for correct date parsing during OCR. Use a valid TZ identifier (e.g., 'Europe/London', 'America/New_York').
ocr_language15'eng'Tesseract OCR language code. Combine multiple with + (e.g., 'eng+deu+fra'). Language packs must be installed in the container image.
admin_user15'admin'Username for the auto-created superuser on first startup.
admin_email15'admin@example.com'Email for the auto-created superuser account.

OCR language codes: Use ISO 639-2/T codes as used by Tesseract. Common values: eng (English), deu (German), fra (French), spa (Spanish), ita (Italian), nld (Dutch), por (Portuguese). The language pack must be present in the container — the default image includes a standard set; custom builds can add additional packs.

C. Tika & Gotenberg (Optional)

Paperless-ngx optionally integrates with Tika (Apache) for content extraction from office documents and Gotenberg for document format conversion. These are disabled by default (PAPERLESS_TIKA_ENABLED=false).

To enable, add them as additional Cloud Run services via additional_services and set PAPERLESS_TIKA_ENABLED=true in environment_variables:

additional_services = [
{
name = "gotenberg"
image = "gotenberg/gotenberg:8"
port = 3000
cpu_limit = "1000m"
memory_limit = "512Mi"
ingress = "INGRESS_TRAFFIC_ALL"
output_env_var_name = "PAPERLESS_TIKA_GOTENBERG_ENDPOINT"
},
{
name = "tika"
image = "apache/tika:latest"
port = 9998
cpu_limit = "1000m"
memory_limit = "512Mi"
ingress = "INGRESS_TRAFFIC_ALL"
output_env_var_name = "PAPERLESS_TIKA_ENDPOINT"
}
]

environment_variables = {
PAPERLESS_TIKA_ENABLED = "true"
}

D. Backup Import & Recovery

When enable_backup_import = true, a dedicated Cloud Run Job restores an existing database backup into the provisioned Cloud SQL PostgreSQL instance. This runs after the db-init job.

VariableGroupDefaultDescription
backup_schedule7'0 2 * * *'Cron expression (UTC) for automated daily backups.
backup_retention_days77Days to retain backup files in GCS.
enable_backup_import7falseTriggers a one-time restore on apply. Set false after a successful import.
backup_source7'gcs''gcs' (full GCS URI) or 'gdrive' (Drive file ID).
backup_uri7""Full GCS URI (e.g., 'gs://my-bucket/paperless-backup.sql') or Google Drive file ID.
backup_format7'sql'Backup format. Options: sql, tar, gz, tgz, tar.gz, zip.

E. Observability & Alerting

A Cloud Monitoring uptime check polls the Paperless-ngx endpoint from multiple global locations. Custom alert policies can monitor Cloud Run metrics (latency, error rate, instance count) and notify support_users.

VariableGroupDefaultDescription
uptime_check_config14{ enabled=false, path="/" }Uptime check configuration. Disabled by default.
alert_policies14[]Metric alert policies. Each: name, metric_type, comparison, threshold_value, duration_seconds, aggregation_period.

9. Exploring with the GCP Console

After a successful deployment, the following GCP Console areas provide the most insight into a running Paperless-ngx instance.

Cloud Run — Service Details Navigate to Cloud Run → Services → paperless-<deployment-id>. The Revisions tab shows all deployed revisions, their traffic percentage, and the container image tag. Click a revision to see its environment variables (redacted for secrets) and resource limits. The Logs tab streams combined stdout/stderr from the gunicorn server and the Celery worker.

Cloud Run — Service URL The URL field on the service overview is the direct *.run.app address for Paperless-ngx. Open it in a browser to reach the login page. Log in with the admin_user username and the password retrieved from Secret Manager (see §11).

Secret Manager Navigate to Security → Secret Manager. Filter by the application_name prefix. You will see:

  • paperless-admin-password-<deployment-id> — the initial admin password.
  • paperless-secret-key-<deployment-id> — the Django application secret key.
  • paperless-db-password-<deployment-id> — the PostgreSQL application user password.

Click any secret and select View secret value (latest version) to retrieve the credential.

Cloud SQL Navigate to SQL → Instances. The Paperless-ngx instance is named app<name><tenant><id> following the repo naming convention. Click the instance, then:

  • Databases tab: confirms the paperless database was created by db-init.
  • Users tab: confirms the paperless user was created.
  • Connections tab: shows active connections from the Cloud Run Auth Proxy sidecar.
  • Operations tab: shows the history of all Cloud SQL operations, including database and user creation.

Cloud Storage Navigate to Cloud Storage → Buckets. The paperless-media-<deployment-id> bucket holds all persistent document data: processed PDFs, originals, thumbnails, and OCR-extracted text. Browse the bucket to verify documents are being stored. The bucket uses STANDARD storage class with public access prevention enforced.

Artifact Registry Navigate to Artifact Registry → Repositories. The paperless repository stores the custom Paperless-ngx container image. The Images tab shows tagged versions with timestamps and digest hashes.

Cloud Monitoring — Uptime Checks Navigate to Monitoring → Uptime checks. The uptime check for the Paperless-ngx login page shows availability across global check locations and response times. Failures here alert support_users via email.

Cloud Build Navigate to Cloud Build → History. Each deployment run appears here with build logs. The db-init Cloud Run Job execution is triggered as part of the apply pipeline.


10. Exploring with gcloud

The following commands are useful for inspecting and operating a Paperless-ngx Cloud Run deployment. Replace PROJECT_ID, REGION, and DEPLOYMENT_ID with your values.

# List all Cloud Run services in the project
gcloud run services list \
--project=PROJECT_ID \
--region=REGION \
--format="table(name,status.url,status.latestReadyRevisionName,status.conditions[0].status)"

# Describe the Paperless-ngx service and show the current URL
gcloud run services describe paperless-DEPLOYMENT_ID \
--project=PROJECT_ID \
--region=REGION \
--format="value(status.url)"

# Stream live logs from the Paperless-ngx service (last 5 minutes)
gcloud logging read \
'resource.type="cloud_run_revision" AND resource.labels.service_name="paperless-DEPLOYMENT_ID"' \
--project=PROJECT_ID \
--freshness=5m \
--format="table(timestamp,textPayload)" \
--order=asc

# List all revisions of the Paperless-ngx service
gcloud run revisions list \
--project=PROJECT_ID \
--region=REGION \
--service=paperless-DEPLOYMENT_ID \
--format="table(name,status.conditions[0].status,spec.containers[0].image,metadata.creationTimestamp)"

# Get the admin password from Secret Manager
gcloud secrets versions access latest \
--secret="paperless-admin-password-DEPLOYMENT_ID" \
--project=PROJECT_ID

# Get the Django secret key from Secret Manager
gcloud secrets versions access latest \
--secret="paperless-secret-key-DEPLOYMENT_ID" \
--project=PROJECT_ID

# List all secrets created for this deployment
gcloud secrets list \
--project=PROJECT_ID \
--filter="name:paperless" \
--format="table(name,replication.automatic,createTime)"

# Describe the Cloud SQL instance
gcloud sql instances describe APP_SQL_INSTANCE_NAME \
--project=PROJECT_ID \
--format="table(name,databaseVersion,settings.tier,ipAddresses[0].ipAddress,state)"

# List databases on the Cloud SQL instance
gcloud sql databases list \
--instance=APP_SQL_INSTANCE_NAME \
--project=PROJECT_ID

# List Cloud SQL users
gcloud sql users list \
--instance=APP_SQL_INSTANCE_NAME \
--project=PROJECT_ID

# List GCS buckets for the deployment (filter by name prefix)
gcloud storage buckets list \
--project=PROJECT_ID \
--filter="name:paperless" \
--format="table(name,location,storageClass,timeCreated)"

# List objects in the paperless-media bucket (document store)
gcloud storage ls gs://paperless-media-DEPLOYMENT_ID/

# List objects in the media/documents directory
gcloud storage ls gs://paperless-media-DEPLOYMENT_ID/documents/

# Check Memorystore Redis instance (if using dedicated Redis)
gcloud redis instances list \
--region=REGION \
--project=PROJECT_ID \
--format="table(name,host,port,state,memorySizeGb,authEnabled)"

# List Cloud Run Jobs (db-init and any custom jobs)
gcloud run jobs list \
--project=PROJECT_ID \
--region=REGION \
--format="table(name,status.latestCreatedExecution.name,metadata.creationTimestamp)"

# Execute the db-init job manually (e.g., for re-initialisation)
gcloud run jobs execute paperless-db-init-DEPLOYMENT_ID \
--project=PROJECT_ID \
--region=REGION

# Get the execution status of a Cloud Run Job
gcloud run jobs executions list \
--job=paperless-db-init-DEPLOYMENT_ID \
--project=PROJECT_ID \
--region=REGION \
--format="table(name,completionStatus,startTime,completionTime)"

# Check Cloud Monitoring uptime check results
gcloud monitoring uptime list-configs \
--project=PROJECT_ID \
--format="table(displayName,httpCheck.path,period,timeout)"

# List Artifact Registry images for the paperless repository
gcloud artifacts docker images list REGION-docker.pkg.dev/PROJECT_ID/paperless \
--project=PROJECT_ID \
--format="table(package,version,tags,createTime)"

# Update the Cloud Run service's traffic to a specific revision (canary)
gcloud run services update-traffic paperless-DEPLOYMENT_ID \
--project=PROJECT_ID \
--region=REGION \
--to-revisions=paperless-DEPLOYMENT_ID-REVISION=10,LATEST=90

# Send all traffic back to latest
gcloud run services update-traffic paperless-DEPLOYMENT_ID \
--project=PROJECT_ID \
--region=REGION \
--to-latest

# Check IAM bindings on the Cloud Run service
gcloud run services get-iam-policy paperless-DEPLOYMENT_ID \
--project=PROJECT_ID \
--region=REGION

11. Platform-Managed Behaviours

The following behaviours are applied automatically by Paperless CloudRun regardless of variable values.

BehaviourImplementationDetail
PostgreSQL 15 requireddatabase_type = "POSTGRES_15" set by Paperless CommonPaperless-ngx requires PostgreSQL. MySQL and SQLite are not supported.
Admin password auto-generatedPAPERLESS_ADMIN_PASSWORD secret provisioned by Paperless CommonThe initial admin password is stored in Secret Manager. Retrieve it via gcloud secrets versions access latest --secret=paperless-admin-password-<id>.
Django secret key auto-generatedPAPERLESS_SECRET_KEY secret provisioned by Paperless CommonRequired for Django session signing. Regenerating this key invalidates all existing user sessions.
GCS Fuse auto-mountedpaperless-media bucket mounted at /usr/src/paperless/mediaWhen gcs_volumes = [], Paperless Common automatically provisions and mounts the media bucket. Requires gen2.
Redis requiredenable_redis = true defaultPaperless-ngx will not start without Redis. When redis_host = "", the NFS server IP is used.
min_instance_count = 0Default in variables.tf (scale-to-zero)Cost-first default. Set to 1+ to keep the consumption pipeline alive without cold-start delay.
Image mirroring enabledenable_image_mirroring = true defaultGHCR images are mirrored to Artifact Registry to avoid rate limits and to satisfy Binary Authorization requirements.
Default db-init jobSupplied by Paperless Common when initialization_jobs = []PostgreSQL database and user are created automatically. Override with a non-empty list to replace.
Scripts directoryscripts_dir = abspath("${module.paperless_app.path}/scripts")Initialization scripts are sourced from Paperless Common, not from the deployment directory.

12. Variable Reference

All user-configurable variables exposed by Paperless CloudRun, sorted by UI group.

VariableGroupDefaultDescription
project_id1GCP project ID. Required.
region1'us-central1'GCP region for resource deployment.
tenant_deployment_id2'demo'Short suffix appended to all resource names.
support_users2[]Email addresses for monitoring alerts.
resource_labels2{}Labels applied to all provisioned resources.
application_name3'paperless'Base resource name. Do not change after initial deployment.
display_name3'Paperless-ngx - Document Management System'Human-readable name for dashboards.
description3'Paperless-ngx - open-source document management system...'Service description.
application_version3'latest'Container image tag. Pin to a specific version for production.
deploy_application4trueSet false for infrastructure-only deployment.
cpu_limit4'2000m'CPU per instance. 2 vCPU recommended for OCR.
memory_limit4'2Gi'Memory per instance.
min_instance_count40Minimum instances. Scale-to-zero by default; set to 1+ to keep consumption pipeline alive.
max_instance_count43Maximum instances. Cost ceiling.
container_port48000Paperless-ngx gunicorn port.
execution_environment4'gen2'Gen2 required for GCS Fuse.
timeout_seconds4300Max request duration. Increase for large document OCR.
enable_cloudsql_volume4trueConnects via Cloud SQL Auth Proxy Unix socket.
enable_image_mirroring4trueMirrors GHCR image to Artifact Registry.
container_protocol4'http1''http1' or 'h2c'.
traffic_split4[]Canary/blue-green traffic allocation.
max_revisions_to_retain47Maximum Cloud Run revisions to keep.
cloudsql_volume_mount_path4'/cloudsql'Container path for Auth Proxy Unix socket.
service_annotations4{}Advanced Cloud Run annotations.
service_labels4{}Labels applied to the Cloud Run service.
ingress_settings5'all''all', 'internal', or 'internal-and-cloud-load-balancing'.
vpc_egress_setting5'PRIVATE_RANGES_ONLY''PRIVATE_RANGES_ONLY' or 'ALL_TRAFFIC'.
enable_iap5falseEnables IAP natively on the Cloud Run service.
iap_authorized_users5[]Users/SAs granted IAP access.
iap_authorized_groups5[]Google Groups granted IAP access.
environment_variables6{}Plain-text environment variables for Paperless-ngx configuration.
secret_environment_variables6{}Secret Manager references.
secret_propagation_delay630Seconds to wait after secret creation.
secret_rotation_period6'2592000s'Secret Manager rotation notification frequency.
backup_schedule7'0 2 * * *'Cron expression (UTC) for automated backups.
backup_retention_days77Days to retain backup files in GCS.
enable_backup_import7falseTriggers a one-time restore on apply.
backup_source7'gcs''gcs' (full URI) or 'gdrive' (file ID).
backup_uri7""Full GCS URI or Google Drive file ID.
backup_format7'sql'Backup format. Options: sql, tar, gz, tgz, tar.gz, zip.
enable_cicd_trigger8falseProvisions a Cloud Build GitHub trigger.
github_repository_url8""Full HTTPS URL of the GitHub repository.
github_token8""GitHub PAT. Required on first apply. Sensitive.
github_app_installation_id8""GitHub App installation ID.
cicd_trigger_config8{ branch_pattern = "^main$" }Advanced Cloud Build trigger config.
enable_cloud_deploy8falseProvisions a Cloud Deploy progressive delivery pipeline.
cloud_deploy_stages8[dev, staging, prod(approval)]Ordered Cloud Deploy promotion stages.
enable_binary_authorization8falseEnforces image attestation on deployment.
enable_custom_sql_scripts9falseRuns SQL scripts from GCS after provisioning.
custom_sql_scripts_bucket9""GCS bucket containing SQL scripts.
custom_sql_scripts_path9""Path prefix within the bucket.
custom_sql_scripts_use_root9falseRun scripts as the root DB user.
enable_cloud_armor10falseProvisions Global HTTPS LB + Cloud Armor WAF.
admin_ip_ranges10[]CIDR ranges exempted from WAF rules.
application_domains10[]Custom domains with Google-managed SSL certificates.
enable_cdn10falseEnables Cloud CDN on the HTTPS LB backend.
max_images_to_retain107Maximum recent container images in Artifact Registry.
delete_untagged_images10trueDeletes untagged images from Artifact Registry.
image_retention_days1030Days after which images are eligible for deletion.
create_cloud_storage11trueSet false to skip GCS bucket creation.
storage_buckets11[]Additional GCS buckets to provision.
enable_nfs11trueProvisions NFS server. Used as Redis host when redis_host is blank.
nfs_mount_path11'/mnt/nfs'Container path for NFS mount.
nfs_instance_name11""Existing NFS GCE VM name. Leave empty to auto-discover.
nfs_instance_base_name11'app-nfs'Base name for inline NFS VM.
gcs_volumes11[]GCS Fuse volume mounts. Empty auto-mounts paperless-media at /usr/src/paperless/media.
manage_storage_kms_iam11falseCreates CMEK KMS key for storage buckets.
enable_artifact_registry_cmek11falseCreates Artifact Registry KMS key.
database_type12'POSTGRES_15'PostgreSQL version. Do not change — Paperless-ngx requires PostgreSQL.
db_name12'paperless'PostgreSQL database name. Do not change after initial deployment.
db_user12'paperless'PostgreSQL application user.
database_password_length1232Auto-generated password length. Range: 16–64.
enable_auto_password_rotation12falseAutomated zero-downtime password rotation.
rotation_propagation_delay_sec1290Seconds to wait after rotation before restarting.
initialization_jobs13[]One-shot Cloud Run Jobs. Empty: Paperless Common supplies default db-init.
cron_jobs13[]Recurring scheduled Cloud Run Jobs.
additional_services13[]Additional Cloud Run services (e.g., Gotenberg, Tika).
startup_probe14{ path="/", initial_delay_seconds=60, failure_threshold=60, ... }Startup probe. Long failure threshold for first-boot migrations.
liveness_probe14{ path="/", initial_delay_seconds=60, failure_threshold=3, ... }Liveness probe.
uptime_check_config14{ enabled=false, path="/" }Optional Cloud Monitoring uptime check.
alert_policies14[]Cloud Monitoring metric alert policies.
time_zone15'UTC'Timezone for document timestamps. Important for OCR date parsing.
ocr_language15'eng'Tesseract OCR language. Combine with + for multiple languages.
admin_user15'admin'Initial admin account username.
admin_email15'admin@example.com'Initial admin account email.
enable_redis21trueRequired. Redis for Celery task queue.
redis_host21""Redis hostname/IP. Defaults to NFS server IP when empty.
redis_port21'6379'Redis TCP port (string).
redis_auth21""Redis AUTH password. Sensitive.
enable_vpc_sc22falseRegisters API calls within the project's VPC-SC perimeter.
vpc_cidr_ranges22[]VPC subnet CIDR ranges for VPC-SC network access level.
vpc_sc_dry_run22trueLogs VPC-SC violations without blocking. Set false to enforce.
organization_id22""GCP Organization ID for VPC-SC. Auto-discovered when empty.
enable_audit_logging22falseEnables detailed Cloud Audit Logs.

13. Outputs

OutputDescription
service_nameName of the Cloud Run service.
service_urlPublic URL of the Cloud Run service.
service_locationGCP region where the Cloud Run service is deployed.
project_idGCP project ID.
deployment_idDeployment ID suffix used in resource names.
database_instance_nameName of the Cloud SQL PostgreSQL instance.
database_nameName of the application database.
database_userName of the application database user.
database_password_secretSecret Manager secret name for the database password.
storage_bucketsCreated GCS storage buckets.
load_balancer_ipStatic IP address of the load balancer.
load_balancer_urlHTTPS URL to access the application via the load balancer.
container_imageContainer image used for the deployment.
cicd_enabledWhether the CI/CD pipeline is enabled.
github_repository_urlGitHub repository URL connected for CI/CD.

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).

VariableSensible DefaultRiskConsequence of Incorrect Value
project_id(required)CriticalNo default — deployment fails immediately.
database_type"POSTGRES_15"CriticalPaperless-ngx requires PostgreSQL. Changing to MySQL or NONE will cause a startup failure — Paperless-ngx's ORM is configured for PostgreSQL exclusively.
enable_redistrueCriticalRedis is mandatory. Without Redis, Paperless-ngx cannot start its Celery workers — the consumption pipeline, OCR processing, and document classification are all inoperable. Documents uploaded via the web UI will queue indefinitely.
redis_host"" (auto-resolves to NFS IP)HighRelies on NFS server IP when blank. If enable_nfs = false and redis_host remains empty with enable_redis = true, Paperless-ngx will fail to connect to Redis at startup.
enable_nfstrueHighThe NFS server IP is used as the default Redis host. Disabling NFS without providing an explicit redis_host causes Redis connection failures at startup.
execution_environment'gen2'CriticalGCS Fuse requires Gen2. Switching to 'gen1' while GCS volumes are mounted causes Cloud Run revisions to fail to start. The paperless-media GCS Fuse volume is always mounted when gcs_volumes = [].
gcs_volumes[] (auto-mounts paperless-media)CriticalThe paperless-media bucket is the sole persistent storage for all Paperless-ngx documents. If the auto-mount is overridden incorrectly, documents are written to the ephemeral container filesystem and lost on the next revision deployment or instance restart.
min_instance_count0 (default; scale-to-zero)HighWhen the instance is cold, uploaded documents are not consumed until the next request warms the service. For unattended consumption directory workflows, this can result in hours of unprocessed documents — set to 1+ if this matters.
db_name"paperless"CriticalImmutable after first deployment — changing this causes Terraform to recreate the database, destroying all document metadata, tags, correspondents, and settings. Physical document files in GCS survive but become unreferenced.
db_user"paperless"CriticalImmutable after first deployment — changing this recreates the Cloud SQL user and invalidates all stored credentials.
application_version"latest"MediumUsing latest means each Cloud Build run may pull a new Paperless-ngx version, which can include database migrations or breaking changes. Pin to a specific version (e.g., "2.13.5") for production deployments.
ocr_language"eng"MediumSet to match the primary language of scanned documents. Incorrect language codes result in poor OCR quality and failed full-text indexing. Multiple languages ("eng+deu") increase OCR time.
time_zone"UTC"MediumIncorrect timezone causes document date parsing errors during OCR — dates on scanned documents may be assigned to the wrong day. Set to match your organisation's timezone.
memory_limit"2Gi"HighOCR on multi-page, high-DPI scans requires significant memory. Reducing below 1Gi causes Python OOM errors during Tesseract processing. For heavy OCR workloads, 4Gi is recommended.
timeout_seconds300MediumOCR on large, multi-page PDFs can take longer than 300 seconds. Cloud Run will terminate requests exceeding timeout_seconds. Consider setting 600 or higher for archives containing large document batches.
backup_retention_days7MediumSeven days is insufficient for a document archive. Loss of more than 7 days of database changes means losing all document metadata (tags, correspondents, custom fields, notes) added during that period. Increase to 30+ days for production.
enable_cloud_armorfalseMediumWithout Cloud Armor, the Paperless-ngx login page is exposed directly to the internet. Document management systems often contain sensitive financial and personal documents. WAF protection is strongly recommended for any internet-accessible deployment.
enable_iapfalseMediumIAP adds Google identity-based authentication in front of Paperless-ngx. For internal document archives, IAP prevents brute-force attacks against the Paperless-ngx login page entirely.
startup_probe failure_threshold60HighPaperless-ngx applies database migrations on startup. 60 × 10s = 600 seconds total startup tolerance. Reducing failure_threshold below 30 causes Cloud Run to kill the container before migrations complete on first deploy, creating a restart loop.

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.