02.01 The Upgrade Process
How SimpleRisk upgrades — the version stamp in version.php, the $releases chain in upgrade.php, the upgrade_from_ functions that walk the chain forward, the database version tracked in the settings table, and the deployment-shape-specific workflows in the rest of this chapter.
Why this matters
SimpleRisk releases roughly every two to three months. Each release ships application code changes, sometimes ships database schema changes, and accumulates against the prior release rather than replacing it. Upgrades are how an installation moves from one release to the next; the upgrade is what runs the schema migrations that keep the database in sync with the application code.
Understanding the upgrade architecture matters because it shapes the operator's mental model for everything else in this chapter. Once you know that:
- The application carries an
APP_VERSIONconstant insimplerisk/includes/version.php. - The database carries a
db_versionvalue in thesettingstable. - The upgrade walks the chain of
upgrade_from_functions in($db) simplerisk/includes/upgrade.phpuntil those two values match. - The chain is a list of every released version (the
$releasesarray near the top ofupgrade.php). - Each function in the chain does its specific upgrade work and atomically updates
settings.db_versionto the next version in the chain.
— then the deployment-specific upgrade workflows (Upgrading via Docker, Upgrading on Bare Metal, Upgrading Extras) all make sense as variations on the same underlying mechanism.
The other thing worth knowing: upgrades are not optional. SimpleRisk's release cadence includes security patches that don't backport to old versions. An installation running on a version released two years ago is exposed to whatever security issues have been fixed since. The upgrade discipline is part of the program's security posture, not an optional convenience.
The third thing: Extras upgrade separately from Core. Each Extra has its own version-tracking and its own upgrade chain (
in settings, upgrade_
function in the Extra). A Core upgrade doesn't automatically bring the Extras forward — they have their own workflow, documented in Upgrading Extras.
How frameworks describe this
Frameworks generally don't prescribe specific upgrade processes (that's vendor-specific) but they do require organizations to keep their software current as part of broader vulnerability management.
- NIST SP 800-53 SI-2 (Flaw Remediation) explicitly mandates that the organization identify, report, and correct information system flaws, including patches and updates. SimpleRisk upgrades fit under SI-2 directly.
- CIS Critical Security Controls v8 Control 7 (Continuous Vulnerability Management) includes patching as a core practice.
- PCI DSS v4.0 Requirement 6.3.3 mandates that all in-scope system components have all known security vulnerabilities addressed within an organization-defined timeframe (typically 30 days for critical, 90 days for high).
- ISO/IEC 27001 Annex A
A.8.8(Management of technical vulnerabilities) requires the organization to have a documented vulnerability-management process that includes timely patching.
For programs running SimpleRisk under any of these regimes, "we're still on the version from 18 months ago" isn't a defensible answer. The upgrade cadence needs to keep pace with the release cadence, with documented exception handling for cases where an upgrade has to wait.
How SimpleRisk implements this
Version stamps
Two version values matter:
APP_VERSION— defined insimplerisk/includes/version.phpas a PHP constant. The format isYYYYMMDD-VVV(e.g.,20260422-001). This is the version of the application code currently deployed.db_version— stored in thesettingstable with keydb_versionand value in the sameYYYYMMDD-VVVformat. This is the version the database schema is currently at.
These two values may not match for short windows: when you replace the application files (Docker image swap, or tar-extract on bare metal) but haven't yet run the database upgrade, APP_VERSION is the new release and db_version is the old release. The upgrade workflow brings db_version up to match APP_VERSION.
The two helpers that read these values:
current_version("app")— returnsAPP_VERSION.current_version("db")— returns thedb_versionfrom the settings table.
The $releases chain
simplerisk/includes/upgrade.php opens with a global $releases array listing every released version in order, oldest first. The current array holds versions stretching back to 20140728-001 and forward to the current release. Each entry corresponds to an upgrade_from_
function defined later in the same file.
The function naming convention is the version string with the dashes removed:
- Version
20260422-001→upgrade_from_20260422001($db) - Version
20260224-001→upgrade_from_20260224001($db)
Each function does the schema migration work for the version it's named after (CREATE TABLE, ALTER TABLE, UPDATE settings SET ..., seed-data inserts, whatever the release needs) and ends with a call to update_database_version($db, $version_to_upgrade, $version_upgrading_to) that atomically moves db_version to the next entry in the $releases chain.
How upgrade_database() walks the chain
The driver function is upgrade_database(). It runs in a recursive pattern:
- Read the current
APP_VERSIONfromversion.php. - Read the current
db_versionfrom thesettingstable. - If they're equal, the database is up to date. Return.
- Find the upgrade function for the current
db_versionviaget_database_upgrade_function_for_release(). This returns the function nameupgrade_from_that knows how to move from the current version to the next. - Invoke that function via
call_user_func(). - The function does its work, then calls
update_database_version()to bumpdb_versionto the next entry in the chain. - Recurse: call
upgrade_database()again. The newdb_versionis now compared againstAPP_VERSION; if they match, return; otherwise, continue the chain.
The recursion handles the case where the application has jumped multiple versions (e.g., the operator skipped two releases and is now upgrading three versions at once). Each upgrade function in the chain runs in turn until the database catches up.
The atomicity comes from the update_database_version() helper, which uses a conditional UPDATE (UPDATE settings SET value=:to WHERE name='db_version' AND value=:from). If the current db_version doesn't match what the upgrade function expected (because another concurrent upgrade is in progress), the UPDATE affects zero rows and the function detects the conflict, preventing partial states.
The upgrade UI and API
Two operator-facing entry points:
/admin/upgrade.php— the browser-based upgrade UI. Requires admin login. Walks the schema upgrade and reports progress. Use this for interactive upgrades.GET /api/upgrade/upgrade/simplerisk/db— the API entry point for programmatic upgrades. Authenticates via the standard API key mechanism. Use this for scripted upgrades or for orchestration in CI/CD-driven deployments.
Several related API routes exist for full-stack upgrade orchestration:
GET /api/upgrade/version/app— get the currentAPP_VERSION.GET /api/upgrade/backup/app— back up the application files (used in the standard upgrade workflow).GET /api/upgrade/backup/db— back up the database (used in the standard upgrade workflow).GET /api/upgrade/upgrade/simplerisk/app— upgrade the application files (Docker image swap, file replacement; the API endpoint orchestrates this for some deployment shapes).GET /api/upgrade/upgrade/simplerisk/db— run the database upgrade (the main programmatic entry point).- Per-extra:
GET /api/for each Extra's upgrade./upgrade/app
These endpoints live in the Upgrade Extra at simplerisk/extras/upgrade/includes/api.php (which itself is an Extra and may need its own activation; for installations that don't activate it, the browser-based /admin/upgrade.php is the upgrade path).
Special handling during upgrade
The upgrade flow deliberately bypasses several security checks that apply during normal operation:
- Account lockout after failed login attempts — disabled during upgrade, so an admin locked out of normal sessions can still log in to run an upgrade.
- MFA enforcement — disabled during upgrade.
- Forced password change — disabled during upgrade.
The reasoning: an upgrade is by definition a recovery scenario where the database schema may be mid-migration; requiring the full normal-operation security flow could prevent the operator from completing the upgrade. The trade-off is documented; if your security policy doesn't permit the bypass, run upgrades from a controlled environment with the same physical/network controls you'd require for any privileged operation.
The upgrade flow also runs in its own session namespace (SimpleRiskDBUpgrade rather than the main app session) so an upgrade in progress doesn't contaminate normal user sessions.
Post-upgrade housekeeping
After the chain of upgrade functions completes, the upgrade flow runs two normalization passes:
convert_tables_to_innodb($db)— alters all tables (exceptsessions) to InnoDB engine. Some older tables may have been MyISAM from earlier installations; this brings them all to InnoDB for transactional consistency.convert_tables_to_utf8($db)— alters all tables toutf8mb4_general_cicollation. Ensures the database can handle the full Unicode range that the multi-language UI requires.
These are idempotent (running them on already-converted tables is a no-op) so they're safe to re-run.
Database privilege requirements
The upgrade flow runs check_grants($db) early to verify the SimpleRisk MySQL user has the privileges the upgrade needs: SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, REFERENCES, INDEX. These are the same privileges granted during the initial install (Step 6 of the wizard); upgrades don't need additional grants beyond what install needed.
If check_grants() fails (because the privileges have been narrowed since install, or because a different MySQL user is being used), the upgrade returns a clear error listing the missing privileges. Either grant them temporarily for the upgrade or use a different connection that has them.
Common pitfalls
A handful of patterns recur with SimpleRisk upgrades.
-
Skipping the application-files step. The two-part upgrade is "swap the files" (new image / new tarball) plus "run the database upgrade" (the chain of
upgrade_from_*functions). Skipping the file swap leaves the application code at the old version even after the database has migrated; the symptoms range from confusing UI behavior to outright errors. Always do both parts; the deployment-shape-specific articles (Upgrading via Docker, Upgrading on Bare Metal) document the specific sequence. -
Skipping the database upgrade after the file swap. The mirror failure: new application code, old database schema. The application starts erroring as code paths try to use schema elements that don't exist yet. After replacing the application files, run the database upgrade promptly — either via
/admin/upgrade.phpor viaGET /api/upgrade/upgrade/simplerisk/db. -
Upgrading without a backup. The single most common operator regret. The upgrade is generally reliable but the schema changes are not trivially reversible without a database backup. Take a backup before any upgrade; see Database Backup and Restore. The backup is the rollback path; without it, a failed upgrade is recoverable only by running the upgrade forward through whatever issue blocked it.
-
Letting the upgrade chain stretch too far. Skipping releases is supported (the chain handles it), but the longer the chain, the more upgrade functions run in series, and the more chance for a failure mid-chain to leave the database in an indeterminate state. For installations that have skipped many releases, consider upgrading through intermediate versions (release-by-release) rather than jumping the entire gap in one upgrade. The intermediate steps are testable independently.
-
Not reading the release notes. Each release's notes (under
release_notes/in the SimpleRisk repository) document what changed, including upgrade-specific notes (data migrations that take significant time, configuration settings that need attention, deprecations). Reading the notes for the versions you're skipping over is part of the upgrade preparation. -
Forgetting to upgrade the Extras. Core upgrades don't trigger Extra upgrades. After a Core upgrade, walk the activated Extras and verify each is at the version expected for the new Core release. The per-Extra upgrade workflow is in Upgrading Extras.
-
Running the upgrade UI from a session that's about to time out. Long upgrades (multi-version chains, large databases) can take many minutes. A session that times out mid-upgrade can leave the operator unable to monitor the completion. Use the API path for long upgrades, or extend the session timeout temporarily.
-
Running the upgrade against a database with active production load. The schema changes can lock tables or run for minutes; concurrent application traffic against the same tables can fail. For high-traffic installations, schedule the upgrade during a maintenance window with the application disabled (or in maintenance mode) for the duration of the upgrade.
-
Treating the upgrade-mode security bypass as a permanent escape hatch. The bypass exists for upgrade-time scenarios. Don't rely on it for routine admin access; if you're doing admin work outside an actual upgrade, run through the normal authentication flow (with MFA if configured).
Related
- Upgrading via Docker
- Upgrading on Bare Metal
- Upgrading Extras
- Database Backup and Restore
- Log Rotation and Disk Management
- The Cron Jobs
- The Initial Configuration Wizard
- Installing Extras
Reference
- Permission required:
check_adminfor the/admin/upgrade.phpUI; valid API key for the v2 API endpoints. - API endpoint(s):
GET /api/upgrade/version/app(get APP_VERSION);GET /api/upgrade/backup/appandGET /api/upgrade/backup/db(orchestrated backups);GET /api/upgrade/upgrade/simplerisk/app(file-swap, deployment-shape-specific);GET /api/upgrade/upgrade/simplerisk/db(the database upgrade — main programmatic entry point); per-ExtraGET /api/./upgrade/app - Implementing files:
simplerisk/includes/version.php(theAPP_VERSIONconstant);simplerisk/includes/upgrade.php(the$releaseschain, theupgrade_from_functions, the() upgrade_database()driver, theupdate_database_version()andget_database_upgrade_function_for_release()helpers);simplerisk/admin/upgrade.php(the browser UI);simplerisk/extras/upgrade/includes/api.php(the v2 API surface). - Database tables:
settings(thedb_versionandapi_keykeys); the schema of every other table is what the upgrade functions modify. config_settingskeys:db_version(the database version stamp);api_key(used by the API-key-based upgrade authentication).- External dependencies: None for the upgrade itself; outbound HTTPS to the SimpleRisk content sources may be relevant for some Extras' upgrade flows (e.g., the SCF Extra's content updates).