Google Cloud Run + Cloud SQL
Install or upgrade a verified public release with the Cloud Run bundle: Cloud SQL, Secret Manager version pins, staged traffic, and the two Canvas handoffs.
This is the recommended managed deployment path. The versioned Cloud Run bundle installs or upgrades a published image without an application source checkout or Cloud Build. It uses one immutable image digest for the migration job, cleanup job, and application service.
The scripts never print secret values. They pin Secret Manager to the exact
numbered versions in .state/secret-versions.env, run migrations before traffic
moves, and keep a candidate revision at zero traffic until /ready and the public
JWKS endpoint pass.
Requirements
- A billed Google Cloud project and permission to administer Cloud Run, Cloud SQL, IAM, Secret Manager, Cloud Scheduler, and required APIs.
- Current
gcloud,gh, Docker,jq, OpenSSL, andcurlon the operator workstation. - A final public HTTPS origin. The bundle can reserve a stable
run.appURL; setTOOL_URLonly when an approved custom HTTPS origin will front it. - A Canvas administrator and a device-management owner for the two external handoffs.
Download and verify the bundle
Use the release notes for the current version and digest:
export VERSION="X.Y.Z"
curl -fLO "https://github.com/JSB2010/safe-online-exam/releases/download/v${VERSION}/safe-online-exam-${VERSION}-cloud-run.tar.gz"
curl -fLO "https://github.com/JSB2010/safe-online-exam/releases/download/v${VERSION}/safe-online-exam-${VERSION}-cloud-run.tar.gz.sha256"
sha256sum --check "safe-online-exam-${VERSION}-cloud-run.tar.gz.sha256"
tar -xzf "safe-online-exam-${VERSION}-cloud-run.tar.gz"
cd "safe-online-exam-${VERSION}-cloud-run"Authenticate, then run the exact attestation command from the release notes. It
must identify JSB2010/safe-online-exam,
.github/workflows/publish-release-image.yml, the release commit, and the tag.
Guided installation
Start with the bundle's own README.md, then run:
./setup.shThe walkthrough performs a read-only preflight, collects a project/region/resource prefix, generates protected bootstrap material, presents the Cloud SQL profile catalog and cost confirmation, provisions or validates resources, and pauses for Canvas at the right times. Sensitive values use no-echo prompts or protected files, not command-line arguments.
Keep the extracted bundle, BOOTSTRAP_DIRECTORY,
CLIENT_IDENTITY_DIRECTORY, and STATE_DIRECTORY out of /tmp,
/private/tmp, and TMPDIR. The bundle rejects those locations so an operating
system cleanup policy cannot remove the only client identity or deployment record.
It writes generated secrets without a terminal line ending and rejects a supplied
database-password file that contains a line break.
It is safe to resume by stage when different administrators own different parts:
./setup.sh --stage configure
./setup.sh --stage prepare
./setup.sh --stage install
./setup.sh --stage finalizeFor unattended use, follow the exact --non-interactive file-input examples in
the extracted README. Do not turn secrets into shell arguments.
What a new installation creates
With RESOURCE_NAME=safe-online-exam, defaults are:
| Resource | Default |
|---|---|
| Cloud Run service and Cloud SQL instance | safe-online-exam |
| Migration and cleanup jobs | safe-online-exam-migrate, safe-online-exam-cleanup |
| Runtime and scheduler service accounts | safe-online-exam, safe-online-exam-sched |
| PostgreSQL database and user | safe_online_exam |
| Secret Manager prefix | safe_online_exam_ |
Use a short, school-specific suffix for separate installations in one project.
Once Canvas is configured, keep the service name, project, region, and public URL
stable. These portable defaults do not rename the repository's existing
canvas-seb-dev or canvas-seb-prod source-build environments.
Cloud SQL and public access
production-zonal is the bundle's cost-conscious new-install default: Cloud SQL
PostgreSQL 17 Enterprise, one dedicated vCPU, 3.75 GiB, one zone, daily backups,
14 retained backups, seven-day PITR, storage auto-growth, deletion protection, and
connector-only access. It trades automatic cross-zone failover and the HA SLA for
a lower bill. Use ./prepare.sh --list-cloud-sql-profiles before accepting a
billable resource, and check current Google Cloud pricing and terms.
existing-reviewed never creates an instance; the institution owns its regional,
HA, backup, retention, networking, capacity, and deletion-protection review.
On a newly configured project, the bundle waits with bounded retries for the Cloud
SQL Admin API to propagate and for a new instance to become RUNNABLE. If either
wait expires, inspect the existing Google Cloud operation before rerunning
prepare.sh; do not assume a second create request is safe.
Canvas must reach the tool without a Google login. PUBLIC_ACCESS=true grants
roles/run.invoker to allUsers on the application service only. If policy
forbids it, put an approved public HTTPS load balancer in front, set TOOL_URL to
that stable origin, and keep migration and cleanup jobs non-public.
The two Canvas handoffs
After the URL is reserved, the installer stops for the Canvas API OAuth Developer
Key. It then installs a first revision with bootstrap-pending LTI values. Create
the LTI key using ${TOOL_URL}/lti/config, install the app, and run the final
stage with the actual client and deployment IDs. The final stage creates new
numbered versions only for those LTI identifiers, updates jobs, stages a revision,
checks it, and explicitly cuts traffic over.
Do not upload the generated SEB .p12, private key, or password to Cloud Run or
Secret Manager. Move the client-only identity directory into the approved MDM or
vault workflow.
Custom domain and generated Cloud Run URL
When the final public origin uses Cloud Run domain mapping, set TOOL_URL to that
HTTPS origin before the prepare stage. After the service is prepared, run:
./map-domain.sh cloudrun.envThe helper creates or inspects the mapping and reports its DNS records and Ready conditions; it does not change external DNS. Complete domain ownership and DNS, then verify the custom origin's health, readiness, JWKS, and LTI endpoints. Cloud Run domain mapping is a Preview feature, so use an approved load balancer when that is not an acceptable operational dependency.
Leave the generated run.app URL enabled while the installation is finalized.
Set DISABLE_DEFAULT_URL_AFTER_FINALIZE=true only after the custom origin passes
those checks. The final stage verifies its zero-traffic candidate first, cuts over,
rechecks the custom origin, and then disables the generated URL. Later upgrades
preserve that policy: they verify the custom origin, temporarily restore the
generated URL for tagged candidate checks, cut over, and disable it again even if
the candidate exits early.
Generate and upload the Canvas theme loader
After prepare.sh, have the bundle render the exact loader for the configured
public origin:
./canvas-theme-loader.sh cloudrun.envUpload the generated canvas-theme-loader.js as the active Canvas account or
sub-account theme's desktop JavaScript before testing a protected assessment.
It contains no secret material. This theme step is separate from LTI registration;
see Load the detector script for the Canvas-side
procedure.
Upgrade and rollback
Download and verify the next bundle. Preserve cloudrun.env, the bootstrap
directory, .state, and the client identity records; merge new template keys and
replace only APP_VERSION and APP_IMAGE with the new bundle values.
./upgrade.sh cloudrun.envThe helper creates an on-demand Cloud SQL backup, waits for its operation to
finish, and requires the backup status to be SUCCESSFUL before migrations or
traffic changes. It records the prior traffic revision, migrates, updates cleanup,
deploys a no-traffic candidate, checks /ready and JWKS, then moves 100% traffic.
When the generated URL was disabled, it verifies the custom origin, temporarily
enables that URL for the candidate, then verifies the custom origin again and
restores the disabled-URL policy. If a candidate check fails, the prior revision
keeps traffic and guarded cleanup restores the prior URL policy; completed forward
migrations remain.
Application rollback needs the protected rollback record and an explicit
schema-compatibility confirmation. It verifies TOOL_URL when a custom origin is
configured. Restore a backup into a controlled target for schema or data recovery;
never down-migrate or overwrite the active database as a first response.
Post-deployment checks
curl -fsS "$TOOL_URL/health"
curl -fsS "$TOOL_URL/ready"
curl -fsS "$TOOL_URL/.well-known/jwks.json"
curl -fsS "$TOOL_URL/lti/config"
curl -fsS "$TOOL_URL/js/canvas-seb-detector.js"Also inspect the active revision and digest, migration and cleanup executions, Cloud Scheduler, backup/PITR state, numbered secret references, and application errors. These route checks do not replace the role-based Canvas and SEB acceptance run.
Deploy the application
Install a verified Safe Online Exam release on Cloud Run or Docker Compose, then preserve the migration, cleanup, backup, and recovery contract.
Docker Compose on Linux
Install or upgrade a verified Safe Online Exam release bundle on a Linux host with PostgreSQL, file secrets, optional Caddy TLS, cleanup, and backup safeguards.