Certification track: Associate Cloud Engineer (ACE)
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 = 0by default (scale-to-zero) — set to1or 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/mediafor 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 byPaperless Common:PAPERLESS_ADMIN_PASSWORDandPAPERLESS_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
| Variable | Group | Type | Default | Description |
|---|---|---|---|---|
project_id | 1 | string | — | GCP project ID. Required. |
tenant_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 | 'paperless' | Base resource name. Do not change after initial deployment. |
display_name | 3 | string | 'Paperless-ngx - Document Management System' | Human-readable name shown in dashboards. |
description | 3 | string | 'Paperless-ngx - open-source document management system with OCR...' | Service description. |
application_version | 3 | string | '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:
| Secret | Description |
|---|---|
PAPERLESS_ADMIN_PASSWORD | Password for the initial admin account. Auto-generated and stored in Secret Manager. |
PAPERLESS_SECRET_KEY | Django 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 and cpu_always_allocated = false by default. Like most modules in this repository, Paperless-ngx scales to zero and bills on a request basis by default (cost-first). The trade-off: the background document consumer + OCR worker only run while an instance is warm, so documents dropped into the consumption directory may sit unprocessed until the next request wakes the service. Setting min_instance_count = 1 alone is not sufficient to restore continuous processing — request-based billing (cpu_always_allocated = false) still throttles CPU to near-zero between requests. Set both cpu_always_allocated = true and min_instance_count = 1 or more to keep the consumption pipeline continuously listening with allocated CPU.
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.
| Variable | Group | Default | Description |
|---|---|---|---|
deploy_application | 4 | true | Set 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_limit | 4 | '2000m' | CPU per instance. 2 vCPU recommended for OCR workloads. |
memory_limit | 4 | '2Gi' | Memory per instance. 2 Gi minimum; increase for large document batches. |
cpu_always_allocated | 4 | false | false = cost-first cold-start (request-based billing); document consumer + OCR worker stop processing when scaled to zero. Set true and min_instance_count >= 1 together to restore continuous operation. |
min_instance_count | 4 | 0 | Minimum running instances. Scale-to-zero by default; set to 1+ and cpu_always_allocated = true to keep the consumption pipeline active. |
max_instance_count | 4 | 3 | Maximum running instances. Cost ceiling. |
container_port | 4 | 8000 | Paperless-ngx gunicorn port. Do not change without matching the Dockerfile. |
execution_environment | 4 | 'gen2' | Gen2 required for GCS Fuse volume mounts. |
timeout_seconds | 4 | 300 | Max request duration. OCR on large documents can be slow; 300s or higher recommended. |
enable_cloudsql_volume | 4 | true | Default true — connects via Cloud SQL Auth Proxy Unix socket. |
enable_image_mirroring | 4 | true | Mirrors the GHCR image into Artifact Registry. Recommended to avoid rate limits. |
container_protocol | 4 | 'http1' | 'http1' or 'h2c'. |
traffic_split | 4 | [] | Percentage-based canary/blue-green traffic allocation. |
max_revisions_to_retain | 4 | 7 | Maximum Cloud Run revisions to keep. Set 0 to disable. |
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 | Paperless CloudRun | Reason |
|---|---|---|---|
container_port | 8080 | 8000 | Paperless-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_mirroring | false | true | Mirrors 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.
| Variable | Group | Default | Description |
|---|---|---|---|
database_type | 12 | 'POSTGRES_15' | PostgreSQL 15. Do not change — Paperless-ngx requires PostgreSQL. |
db_name | 12 | 'paperless' | PostgreSQL database name. Do not change after initial deployment. |
db_user | 12 | 'paperless' | PostgreSQL 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. |
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'.
| Variable | Group | Default | Description |
|---|---|---|---|
gcs_volumes | 11 | [] | 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_storage | 11 | true | Set false to skip additional GCS bucket creation. |
storage_buckets | 11 | [] | Additional GCS buckets beyond the auto-provisioned media bucket. |
enable_nfs | 11 | true | Provisions NFS server. Used as the Redis host when redis_host is blank. Requires gen2. |
nfs_mount_path | 11 | '/mnt/nfs' | Container path for the NFS mount. |
nfs_instance_name | 11 | "" | Name of an existing NFS GCE VM. Leave empty to auto-discover or provision inline. |
nfs_instance_base_name | 11 | 'app-nfs' | Base name for an inline NFS GCE VM. Deployment ID is appended. |
manage_storage_kms_iam | 11 | false | Creates a CMEK KMS keyring and enables CMEK on all storage buckets. |
enable_artifact_registry_cmek | 11 | false | Creates an Artifact Registry KMS key for at-rest image encryption. |
D. Networking
| 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' | '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:
- Connects to Cloud SQL PostgreSQL via the Auth Proxy Unix socket.
- Creates the
paperlessdatabase user with the password from Secret Manager. - Creates the
paperlessdatabase if it does not exist. - 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:
| Variable | Group | Default | Description |
|---|---|---|---|
initialization_jobs | 13 | [] | One-shot Cloud Run Jobs. Leave empty for Paperless Common to supply the default db-init job. |
cron_jobs | 13 | [] | Recurring jobs triggered by Cloud Scheduler. |
additional_services | 13 | [] | 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.
| 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). |
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.
| 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
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.
| Variable | Group | Default | Description |
|---|---|---|---|
secret_environment_variables | 6 | {} | Map of env var name → Secret Manager secret ID. Resolved at runtime. |
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 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.
| 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 number of recent container images to keep in Artifact Registry. Set 0 to disable. |
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. |
C. Custom Domains
| Variable | Group | Default | Description |
|---|---|---|---|
application_domains | 10 | [] | 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.
| 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. 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 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 = 0means uploaded documents will not be processed until the next request warms the instance.
B. Traffic Splitting
| Variable | Group | Default | Description |
|---|---|---|---|
traffic_split | 4 | [] | 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.
| Variable | Group | Default | Description |
|---|---|---|---|
startup_probe | 14 | { 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_probe | 14 | { 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_config | 14 | { enabled=false, path="/" } | Optional Cloud Monitoring uptime check. 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 PostgreSQL user, and writes a new secret version.
- After
rotation_propagation_delay_secseconds, the job restarts the Paperless-ngx 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. |
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.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_redis | 21 | true | Required. Redis for Celery task queue and result backend. |
redis_host | 21 | "" | Redis server hostname or IP. Defaults to NFS server IP when empty. Override with Memorystore for production. |
redis_port | 21 | '6379' | Redis server TCP port (string). |
redis_auth | 21 | "" | 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.
| Variable | Group | Default | Description |
|---|---|---|---|
time_zone | 15 | '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_language | 15 | 'eng' | Tesseract OCR language code. Combine multiple with + (e.g., 'eng+deu+fra'). Language packs must be installed in the container image. |
admin_user | 15 | 'admin' | Username for the auto-created superuser on first startup. |
admin_email | 15 | '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.
| 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/paperless-backup.sql') or Google Drive file ID. |
backup_format | 7 | '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.
| Variable | Group | Default | Description |
|---|---|---|---|
uptime_check_config | 14 | { enabled=false, path="/" } | Uptime check configuration. Disabled by default. |
alert_policies | 14 | [] | 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
paperlessdatabase was created bydb-init. - Users tab: confirms the
paperlessuser 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.
| Behaviour | Implementation | Detail |
|---|---|---|
| PostgreSQL 15 required | database_type = "POSTGRES_15" set by Paperless Common | Paperless-ngx requires PostgreSQL. MySQL and SQLite are not supported. |
| Admin password auto-generated | PAPERLESS_ADMIN_PASSWORD secret provisioned by Paperless Common | The 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-generated | PAPERLESS_SECRET_KEY secret provisioned by Paperless Common | Required for Django session signing. Regenerating this key invalidates all existing user sessions. |
| GCS Fuse auto-mounted | paperless-media bucket mounted at /usr/src/paperless/media | When gcs_volumes = [], Paperless Common automatically provisions and mounts the media bucket. Requires gen2. |
| Redis required | enable_redis = true default | Paperless-ngx will not start without Redis. When redis_host = "", the NFS server IP is used. |
| min_instance_count = 0, cpu_always_allocated = false | Default in variables.tf (scale-to-zero, request-based billing) | Cost-first default. Set both cpu_always_allocated = true and min_instance_count = 1+ to keep the consumption pipeline alive without cold-start delay — min_instance_count alone is not sufficient. |
| Image mirroring enabled | enable_image_mirroring = true default | GHCR images are mirrored to Artifact Registry to avoid rate limits and to satisfy Binary Authorization requirements. |
| Default db-init job | Supplied by Paperless Common when initialization_jobs = [] | PostgreSQL database and user are created automatically. Override with a non-empty list to replace. |
| Scripts directory | scripts_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.
| Variable | Group | Default | Description |
|---|---|---|---|
project_id | 1 | — | GCP project ID. Required. |
region | 1 | 'us-central1' | GCP region for resource deployment. |
tenant_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 | 'paperless' | Base resource name. Do not change after initial deployment. |
display_name | 3 | 'Paperless-ngx - Document Management System' | Human-readable name for dashboards. |
description | 3 | 'Paperless-ngx - open-source document management system...' | Service description. |
application_version | 3 | 'latest' | Container image tag. Pin to a specific version for production. |
deploy_application | 4 | true | Set false for infrastructure-only deployment. |
cpu_limit | 4 | '2000m' | CPU per instance. 2 vCPU recommended for OCR. |
memory_limit | 4 | '2Gi' | Memory per instance. |
cpu_always_allocated | 4 | false | Cost-first cold-start default. Set true together with min_instance_count >= 1 to restore continuous consumer/OCR processing. |
min_instance_count | 4 | 0 | Minimum instances. Scale-to-zero by default; set to 1+ and cpu_always_allocated = true to keep consumption pipeline alive. |
max_instance_count | 4 | 3 | Maximum instances. Cost ceiling. |
container_port | 4 | 8000 | Paperless-ngx gunicorn port. |
execution_environment | 4 | 'gen2' | Gen2 required for GCS Fuse. |
timeout_seconds | 4 | 300 | Max request duration. Increase for large document OCR. |
enable_cloudsql_volume | 4 | true | Connects via Cloud SQL Auth Proxy Unix socket. |
enable_image_mirroring | 4 | true | Mirrors GHCR image to Artifact Registry. |
container_protocol | 4 | 'http1' | 'http1' or 'h2c'. |
traffic_split | 4 | [] | Canary/blue-green traffic allocation. |
max_revisions_to_retain | 4 | 7 | Maximum Cloud Run revisions to keep. |
cloudsql_volume_mount_path | 4 | '/cloudsql' | Container path for Auth Proxy Unix socket. |
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 environment variables for Paperless-ngx configuration. |
secret_environment_variables | 6 | {} | Secret Manager references. |
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 in Artifact Registry. |
delete_untagged_images | 10 | true | 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 | [] | Additional GCS buckets to provision. |
enable_nfs | 11 | true | Provisions NFS server. Used as Redis host when redis_host is blank. |
nfs_mount_path | 11 | '/mnt/nfs' | Container path for NFS mount. |
nfs_instance_name | 11 | "" | Existing NFS GCE VM name. Leave empty to auto-discover. |
nfs_instance_base_name | 11 | 'app-nfs' | Base name for inline NFS VM. |
gcs_volumes | 11 | [] | GCS Fuse volume mounts. Empty auto-mounts paperless-media at /usr/src/paperless/media. |
manage_storage_kms_iam | 11 | false | Creates CMEK KMS key for storage buckets. |
enable_artifact_registry_cmek | 11 | false | Creates Artifact Registry KMS key. |
database_type | 12 | 'POSTGRES_15' | PostgreSQL version. Do not change — Paperless-ngx requires PostgreSQL. |
db_name | 12 | 'paperless' | PostgreSQL database name. Do not change after initial deployment. |
db_user | 12 | 'paperless' | PostgreSQL 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. |
initialization_jobs | 13 | [] | One-shot Cloud Run Jobs. Empty: Paperless Common supplies default db-init. |
cron_jobs | 13 | [] | Recurring scheduled Cloud Run Jobs. |
additional_services | 13 | [] | Additional Cloud Run services (e.g., Gotenberg, Tika). |
startup_probe | 14 | { path="/", initial_delay_seconds=60, failure_threshold=60, ... } | Startup probe. Long failure threshold for first-boot migrations. |
liveness_probe | 14 | { path="/", initial_delay_seconds=60, failure_threshold=3, ... } | Liveness probe. |
uptime_check_config | 14 | { enabled=false, path="/" } | Optional Cloud Monitoring uptime check. |
alert_policies | 14 | [] | Cloud Monitoring metric alert policies. |
time_zone | 15 | 'UTC' | Timezone for document timestamps. Important for OCR date parsing. |
ocr_language | 15 | 'eng' | Tesseract OCR language. Combine with + for multiple languages. |
admin_user | 15 | 'admin' | Initial admin account username. |
admin_email | 15 | 'admin@example.com' | Initial admin account email. |
enable_redis | 21 | true | Required. Redis for Celery task queue. |
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 network 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. |
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 PostgreSQL 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. |
load_balancer_ip | Static IP address of the load balancer. |
load_balancer_url | HTTPS URL to access the application via the load balancer. |
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. |
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 | "POSTGRES_15" | Critical | Paperless-ngx requires PostgreSQL. Changing to MySQL or NONE will cause a startup failure — Paperless-ngx's ORM is configured for PostgreSQL exclusively. |
enable_redis | true | Critical | Redis 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) | High | Relies 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_nfs | true | High | The 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' | Critical | GCS 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) | Critical | The 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_count | 0 (default; scale-to-zero) | High | When 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. |
cpu_always_allocated | false (default; request-based billing) | High | Even with min_instance_count >= 1, request-based billing throttles CPU to near-zero between requests, so the document consumer + OCR worker effectively stall. Set true together with min_instance_count >= 1 to fully restore continuous background processing. |
db_name | "paperless" | Critical | Immutable 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" | Critical | Immutable after first deployment — changing this recreates the Cloud SQL user and invalidates all stored credentials. |
application_version | "latest" | Medium | Using 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" | Medium | Set 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" | Medium | Incorrect 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" | High | OCR 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_seconds | 300 | Medium | OCR 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_days | 7 | Medium | Seven 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_armor | false | Medium | Without 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_iap | false | Medium | IAP 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_threshold | 60 | High | Paperless-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.
Related guides
- Hands-on lab: Paperless-ngx on Cloud Run — deploy it step by step, with the console screens and commands at each stage.
- Paperless-ngx GKE Module — Configuration Guide — the same application on Kubernetes, for when you need the other deployment target.
- Paperless-ngx Common Shared Configuration Module — the configuration shared by both deployment targets.
- Deployed alongside Odoo on Cloud Run, Metabase on Google Cloud Run, OnlyOffice on Google Cloud Run, Passbolt on Google Cloud Run in the Integrated ERP Platform solution.
Need RAD to do something it does not do yet? Request it on the roadmap, or vote on what is already there.