Safe OnlineExam
Deploy the application

Deployment overview

The two supported ways to run the service, the invariants that hold for both, and how to choose.

The service is a single Node.js process backed by PostgreSQL. It needs a container runtime, PostgreSQL 17+, environment variables or mounted secret files, an HTTPS public origin, a one-shot migration step before each new revision, and a scheduled cleanup job. Everything else is your choice of platform.

Two supported modes

The runtime itself is provider-agnostic — the Google-specific behavior lives only in the checked-in Cloud Build files. Any platform that provides PostgreSQL 17+, public HTTPS ingress, secret injection, an exact-image migration job before traffic, and a scheduled cleanup job can run the same container.

Release invariants (both modes)

These hold no matter how you deploy. Treat them as rules, not suggestions.

The invariants

  • Keep each Canvas environment isolated by URL, database, credentials, secrets, LTI deployment, and runtime identity.
  • Run the exact image's migrations before sending traffic to that image.
  • Deploy immutable image digests in managed environments — never a moving tag.
  • Treat application rollback and schema recovery as separate decisions.
  • Keep PostgreSQL private. Only the HTTPS application endpoint is public.
  • Use /health for process liveness and /ready for database + schema readiness.
  • Do not change an existing service's name/URL unless a Canvas URL change is intentional.

Health and readiness

Two endpoints tell you the state of a revision:

EndpointMeaning
GET /healthThe process is up and listening. Liveness only.
GET /readyPostgreSQL is reachable and every checked-in migration has been applied. Fails until the database is healthy and current.

/ready failing after a deploy almost always means the migration job did not run or did not complete against this image — see Operations and Troubleshooting → Deployment.

The two-pass bootstrap

Both guides deploy twice on first install: once with placeholder LTI IDs to obtain a stable URL and a working /lti/config, then again with the real LTI_CLIENT_ID and LTI_DEPLOYMENT_ID after you register in Canvas. This is because the Canvas registration is served by the running service. The Setup overview shows where this fits in the whole sequence.

What you will produce

By the end of a deploy you will have:

  • A reachable HTTPS service where /health, /ready, /lti/config, and /.well-known/jwks.json all respond.
  • A PostgreSQL 17+ database with all migrations applied.
  • A secret store holding the LTI signing key, session/state secrets, Canvas API credentials, the database password, and the public SEB encryption certificate.
  • A scheduled cleanup job.

Then you move on to Connect Canvas.

Pick your path: Google Cloud or Docker / VPS. Both reference the shared Configuration and Certificate management pages.

On this page