Hi all,
I'm an admin on a self-hosted Canvas LMS installation that has fallen significantly behind on upgrades, and I'm looking for community guidance on the realistic upgrade pathway. Hoping someone here has navigated something similar, or can point me at the right documentation.
Where we are
Our Canvas instance has been running the same commit since February 2021:
- Canvas commit 56e7131 (treesame to origin/stable/2021-02-03)
- Originally Debian 10 + Ruby 2.6 + PostgreSQL 11
- Recently rebuilt the runtime stack: Debian 12, Ruby 3.4.9, Node 20, PostgreSQL 14, Bundler 2.6
- Database has been carried through the rebuild via pg_dump/restore (PG 11 → PG 14 cross-version restore worked cleanly)
The installation has been operationally stable, which is partly why upgrading wasn't prioritized. Now that we're trying to upgrade, the gap has become a substantial problem.
What we've tried- bundle install against current prod branch's Gemfile.lock — succeeds, 414 gems install
- yarn install against current prod workspace deps — succeeds
- Restored a fresh dump of our database into the new PG 14 environment
- Attempted rake db:migrate — blocked immediately
The blockers we've found
ValidateMigrationIntegrity raises on direct upgradedb/migrate/20101201000096_validate_migration_integrity.rb raises with "You are trying to upgrade from a too-old version of Canvas. Please first upgrade to a version that includes database migration 20250930000003."
Mapping the check across all
origin/stable/YYYY-MM-DD branches in the repo, I see the integrity check filename suffix bumps each release window, with a different sentinel migration ID each time. The earliest sentinel we observed is
20180425185812 (on stable branches from early 2024), bumping forward through ~11 different sentinels to the current one.
Our DB has only the
20180425185812 sentinel in
schema_migrations (which makes sense — we're from before the squash system was introduced).
Walking through intermediate stable branches hits a Ruby compatibility wallThe latest stable branch whose sentinel we have applied is
stable/2024-03-13. Bundle install on that branch fails because the Gemfile depends on
ddtrace ~> 1.13, which constrains Ruby < 3.4. We're on 3.4.9 (Canvas's current requirement).
This implies walking through intermediate Canvas versions also requires installing matching Ruby versions for each window — roughly 5-7 Ruby installs via RVM, with the bundle install + migrate cycle done at each step.
Bypassing the integrity check leaves schema incomplete
I experimented with inserting the sentinel into
schema_migrations directly to bypass the gate. Migrations proceeded past the check but quickly failed at
AddDenormalizedFieldsToAccessibilityResourceScans (
20250706135254) with
relation "public.accessibility_resource_scans" does not exist — because the migration that creates that table was part of the squashed range we bypassed.
Faking the sentinel doesn't work because the squash migrations contain the actual table creation work for everything in that range.
What I'm hoping the community can help with
We've reached the conclusion that
no single rake db:migrate will get us from our state to current, and that the options are:
- Chained Canvas walkthrough — install multiple Ruby versions, walk through each monthly stable branch in order, apply migrations at each stop. Multi-week effort with many manual steps.
- Fresh install + bulk data import — set up current Canvas from scratch, use CSV/SIS import to migrate users/courses/enrollments. Likely loses richer content (assignments with feedback, submissions, grade history, discussions).
Before committing to either, I'd appreciate input on:
- Has anyone here navigated an upgrade from an equally old Canvas installation? What did the actual pathway look like? Any gotchas beyond what I've described?
- Is there documented Instructure guidance for upgrading installations that have fallen multiple years behind? My searching hasn't turned up an official "ancient install upgrade" runbook, but I may have missed it.
- What's the typical Canvas → Canvas data migration approach when version skipping isn't an option? Are there community tools that handle deep data export/re-import better than vanilla CSV/SIS import?
- For self-hosted operators currently up-to-date: how often are you upgrading to stay within the squash window? Monthly? Every few releases? Trying to understand the operational cadence I should plan for going forward.
Happy to share more technical detail or specific log output if helpful. The investigation on our side is documented thoroughly internally; can extract relevant excerpts for community discussion.
Thanks in advance for any direction —