Skip to main content
Safe Online Exam’s Google Cloud Run bundle is the recommended managed deployment path for institutions that prefer cloud-managed compute, PostgreSQL, ingress, and secrets. The installer uses plain gcloud, Docker, gh, jq, OpenSSL, cmp, and curl — it does not require a source checkout or Cloud Build. This page covers downloading and verifying the release bundle, understanding the resources the installer creates, running the eight-stage installation flow, selecting a Cloud SQL profile, and upgrading to a new release.
Do not run or extract the release bundle from /tmp, /private/tmp, or TMPDIR. The bootstrap command rejects protected-state paths in those locations so that automatic cleanup cannot remove the only SEB client identity or deployment records. Use a durable installation directory such as /srv/safe-online-exam or /opt/safe-online-exam.

Download and Verify the Bundle

Replace X.Y.Z with the target release version:
Read the extracted README.md before running ./setup.sh. The extracted README contains release-specific notes that take precedence over this general guide.

Resources the Installer Creates

With the default RESOURCE_NAME=safe-online-exam, a new installation provisions the following Google Cloud resources: Use a short suffix (e.g., RESOURCE_NAME=safe-online-exam-staging) when running multiple installations in the same Google Cloud project. Do not rename a service after Canvas has registered its run.app URL — that constitutes a Canvas-facing integration change requiring re-registration.

Installation

1

Review and configure the environment

Before running the installer, set required variables in cloudrun.env. At minimum, configure:
  • PROJECT_ID — your Google Cloud project.
  • REGION — the Cloud Run region (e.g., us-central1).
  • TOOL_URL — the stable public HTTPS origin (Cloud Run URL or custom domain mapping).
  • CANVAS_DOMAIN — the Canvas base origin (e.g., https://canvas.example.edu).
Keep cloudrun.env, bootstrap state, client identity, and .state files outside source control. Restrict their permissions to 0600 or stricter.
2

List available Cloud SQL profiles

Review the versioned profile catalog before accepting any billable Cloud SQL resource:
The default profile is production-zonal. See Cloud SQL Profile Selection below for details on each option.
3

Run the installer (interactive)

Start the guided, resumable installation:
The installer pauses at two Canvas handoff points to let you create Developer Keys in Canvas before it continues. The flow is fully resumable — you can run individual stages if interrupted:
For automated or unattended installs, use the non-interactive file-input examples in the extracted bundle README. Secret values are accepted through no-echo prompts or protected files, never as command-line arguments.
4

Stage 1–2: Preflight and bootstrap

The installer performs a read-only preflight of your local tools and Google Cloud project, then creates the protected bootstrap state and signing/certificate material. The SEB .p12, private key, and passphrase stay in the protected client identity directory; they must never be uploaded to Secret Manager or included in a Cloud Run revision.
5

Stage 3–4: Cloud SQL and stable URL

The installer creates or validates the Cloud SQL instance with the selected profile, then reserves the stable Cloud Run service URL before any traffic is routed. This URL is safe to give to Canvas before the service is fully deployed.
6

Stage 5: Create Canvas API Developer Key (pause)

The installer pauses and displays the stable TOOL_URL. At this point, open Canvas Admin and create the API OAuth Developer Key as described in the Canvas Setup guide. Record the client ID and secret, then return to the installer.
7

Stage 6: Deploy migrations, cleanup, and candidate service

The installer runs the migration job, deploys the cleanup job, and deploys a candidate no-traffic service revision. It verifies health and readiness at the candidate before continuing.
8

Stage 7: Create Canvas LTI Developer Key (pause)

The installer pauses again. Open Canvas Admin and create the LTI 1.3 Developer Key using the configuration URL (${TOOL_URL}/lti/config), then install the external app and record the deployment ID. Return to the installer with both values.
9

Stage 8: Finalize and cut traffic

The installer creates new numbered Secret Manager versions with the final Canvas values, updates the runtime service account’s secret access bindings, and cuts traffic to the finalized revision. Run the post-install smoke checks to confirm the deployment:
Public route checks do not replace a real administrator, instructor, student, Classic Quiz, New Quiz, and managed-client acceptance test.

Cloud SQL Profile Selection

The default production-zonal profile is a cost-conscious dedicated, single-zone PostgreSQL 17 instance with:
  • Automated daily backups and point-in-time recovery.
  • Deletion protection.
  • Encrypted, connector-only access (no public IP).
  • Storage auto-growth.
  • A configured maintenance window.
It does not provide automatic cross-zone failover or the high-availability SLA. List all available profiles to review alternatives before accepting a billable resource:
Use existing-reviewed when the institution supplies an existing PostgreSQL 17 Cloud SQL instance in the same region. In that mode, the institution owns the HA, backup, retention, networking, sizing, and deletion-protection configuration.
Price text in a downloaded bundle is a dated planning reference, not a quote. Check the live Google Cloud pricing and terms before creating or committing to any Cloud SQL resource.

IAM and Public Access

Use separate runtime, scheduler, and deployer service accounts:
  • The runtime service account needs Cloud SQL Client access limited to the configured project and secret access scoped to only the exact secrets it consumes. Do not grant project-wide Secret Manager access.
  • The deployer must be able to read each exact pinned secret version to compare it with the protected bootstrap value before deciding whether to create a new version.
  • The scheduler service account invokes the cleanup job on schedule.
Canvas must reach the service without an interactive Google login. The bundle can grant public invocation only on the application service. If your organization policy prohibits that binding, place an approved public HTTPS load balancer in front and set TOOL_URL to its stable origin. Migration and cleanup jobs must remain non-public.

Custom Domain Mapping

When TOOL_URL is a Cloud Run domain mapping rather than the generated run.app URL, run the bundle’s explicit domain mapping command after prepare.sh:
This command creates or reads the mapping and prints required DNS/Ready conditions but does not change DNS itself. Only set DISABLE_DEFAULT_URL_AFTER_FINALIZE=true after the custom origin has passed health, readiness, JWKS, and LTI checks. Later upgrades temporarily restore the generated URL for candidate verification and then disable it again after the custom origin passes.

Upgrade Procedure

1

Download and verify the new bundle

Follow the same download and checksum verification steps as the initial install. Extract the new bundle to a new directory — do not overwrite the previous bundle.
2

Preserve the existing environment and invoke the new upgrade.sh

Keep cloudrun.env, bootstrap state, client identity, and .state files in their durable installation directory. Merge any new template keys into cloudrun.env rather than overwriting it. Then invoke the new bundle’s upgrade helper with the path to the existing environment file:
3

The upgrade.sh flow

The upgrade helper:
  1. Validates the complete current environment and bootstrap contract; creates any newly required numbered Secret Manager versions; and grants the existing runtime identity access.
  2. Creates an on-demand Cloud SQL backup and requires a SUCCESSFUL status before proceeding.
  3. Runs the new migration job.
  4. Updates the cleanup job and applies the current environment and secret bindings to both jobs.
  5. Deploys a no-traffic candidate revision with those same current bindings.
  6. Temporarily enables the previously disabled generated URL so the tagged candidate can be verified.
  7. Verifies candidate readiness and JWKS.
  8. Cuts traffic to the new revision.
  9. Verifies the custom origin and restores the prior generated-URL policy.
If a candidate check fails, the previous revision retains all traffic. Forward migrations already applied remain in place.
4

Verify the upgrade

After traffic has been cut to the new revision, run the same smoke checks from the install finalization step and complete a real Canvas and SEB acceptance test.
Application rollback does not reverse database migrations. Restore an older image only after confirming schema compatibility. For data recovery, restore a backup into a controlled target first — do not overwrite the active database as an initial diagnostic action.

OAuth Token Encryption Upgrade (Existing Installations)

For an existing installation that may contain plaintext OAuth tokens, use the staged transition. Merge the new template keys into cloudrun.env, then run the upgrade with OAUTH_TOKEN_ENCRYPTION_MODE=compat:
After the compat revision is healthy, set OAUTH_TOKEN_ENCRYPTION_MODE=enforce, deploy the same image again, verify the service, then run the one-shot token rewrite job:
Run it a second time and require 0 updates before removing any retired key. A pre-encryption revision is not a valid rollback target after encrypted writes begin.

Next Steps

After the service is healthy, proceed to Canvas Setup to complete Developer Key registration (if you did not do so during the interactive install), then provision the SEB encryption identity following the Certificate Management guide.