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.
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.