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.
The Compose release bundle runs the published application image, PostgreSQL 17, a one-shot migration service, and an opt-in cleanup service. It does not contain application source or any private SEB client identity material.
Use this path on a current, maintained Linux host with Docker Engine, Docker Compose v2, stable DNS, encrypted storage, time synchronization, monitored disk capacity, and restricted administrative access. Do not publish PostgreSQL. Canvas requires a stable public HTTPS origin.
Download and verify the release
export VERSION="X.Y.Z"
curl -fLO "https://github.com/JSB2010/safe-online-exam/releases/download/v${VERSION}/safe-online-exam-${VERSION}-compose.tar.gz"
curl -fLO "https://github.com/JSB2010/safe-online-exam/releases/download/v${VERSION}/safe-online-exam-${VERSION}-compose.tar.gz.sha256"
sha256sum --check "safe-online-exam-${VERSION}-compose.tar.gz.sha256"
tar -xzf "safe-online-exam-${VERSION}-compose.tar.gz"
cd "safe-online-exam-${VERSION}"Read the extracted README.md, verify the published image attestation using the
release-note command, and keep the exact bundled
ghcr.io/jsb2010/safe-online-exam@sha256:... reference. Never replace it with a
moving tag for production.
Guided installation
Run:
./setup.shThe walkthrough creates and protects .env.secrets, asks whether bundled Caddy or
an existing reverse proxy owns HTTPS, collects the public URL and Canvas values,
generates database/session/LTI/certificate material, accepts the Canvas API secret
without terminal echo, validates the rendered Compose model, and starts only after
confirmation.
For automation, use the bundle's explicit non-interactive contract. Pass sensitive inputs through protected files, never command-line values:
./setup.sh \
--non-interactive \
--bootstrap \
--no-caddy \
--env-file .env.secrets \
--canvas-api-client-secret-file /secure/input/canvas-api-secret--configure-only validates the complete model without pulling or starting
containers. Use --caddy only after setting PUBLIC_HOST and opening ports 80
and 443.
First-install Canvas bootstrap
Canvas cannot supply the LTI client and deployment IDs until it can read the
running service configuration. On the first guided run, enter bootstrap-pending
for both LTI values, but use the real final Canvas origin, public HTTPS URL, and
API OAuth Developer Key.
After the stack and HTTPS are working:
- Create the LTI Developer Key using
${TOOL_URL}/lti/config. - Install the external app and record its client and deployment IDs.
- Run
./setup.shagain and enter the real LTI values. - Confirm
/ready, load the Canvas theme loader, and complete role-based acceptance.
bootstrap-pending is a registration bootstrap value only. Signed LTI launches
cannot work until the real values are saved and the app is recreated.
Runtime shape and HTTPS
The bundle starts PostgreSQL with a persistent postgres_data volume, a one-shot
migrate service, the app, and an opt-in cleanup service. The application
binds to loopback by default. Use an approved reverse proxy and set:
TOOL_URL=https://safe-online-exam.example.edu
CANVAS_REDIRECT_URI=https://safe-online-exam.example.edu/api/oauth2callbackThe optional Caddy profile terminates public HTTPS when PUBLIC_HOST is set. Keep
the app port and PostgreSQL private. Do not use a production HTTP TOOL_URL.
Cleanup and backups
Run cleanup at least daily from a monitored systemd timer or cron job:
docker compose \
--env-file .env.secrets \
-f compose.yaml \
-f compose.secrets.yaml \
--profile maintenance run --rm cleanupBack up the database before high-risk changes and on the institution's documented
schedule. A custom pg_dump is compressed but not encrypted; encrypt it
immediately, move it to approved off-host storage, and remove the plaintext copy.
Restore drills belong in a separate database or isolated host. Validate the
archive, restore with --exit-on-error, run the intended migrations, inspect the
application tables, and exercise an isolated flow. A backup that has never been
restored is not verified.
Upgrade and rollback
Download and checksum the next bundle. Preserve .env.secrets, secrets/, the
client identity record, and the database volume; merge new template keys. Then:
./upgrade.sh .env.secretsThe helper creates and validates a PostgreSQL custom-format backup, pulls the exact pinned images, applies forward migrations, restarts the topology, and checks readiness. Copy the backup to encrypted off-host storage.
Upgrade stored Canvas OAuth tokens from v1.0.x
Existing installations that have Canvas OAuth grants from before v1.1 must use the
release bundle's two-stage token-encryption upgrade. Merge the new keyring and
active-key settings from the bundle template, set
OAUTH_TOKEN_ENCRYPTION_MODE=compat explicitly, and run the upgrade once. The
bundle creates a new keyring only when one is absent; retain all existing protected
settings.
Then set OAUTH_TOKEN_ENCRYPTION_MODE=enforce, recreate and verify the same
image, and rewrite stored rows:
docker compose --env-file .env.secrets -f compose.yaml -f compose.secrets.yaml \
--profile maintenance run --rm encrypt-oauth-tokensRun the rewrite a second time and require zero rows updated before retiring any old
key. Do not restart a pre-encryption image after encrypted token writes: it cannot
read the new rows. The helper rejects a direct enforce upgrade when the preceding
service has not already run in compat or enforce mode.
Application rollback does not reverse database migrations. Only restore an older image after confirming it supports the current schema. Recover data by restoring into a controlled target; never make automatic down-migration the response to a failed upgrade.
Complete the acceptance sequence after installation or any meaningful upgrade.
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.
Configuration reference
Every runtime setting — hardened profiles, database, application values, file-based secrets, Canvas/LTI endpoints, SEB options, and secret rotation.