Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,059 of 9,059 pages
About the help in other languages
All help pages

11. Upgrading

This chapter brings an existing installation to the current Exponential 6.0 line: from an earlier 6.0.x, from 5.4 or 5.90, or from the 4.x and older 5.x lines. It explains how to find the version you run, the one rule…

Download as PDF

Address: https://doc.exponential.earth/exphelp/viewer/install/11-upgrading.md

On this page

11. Upgrading

This chapter brings an existing installation to the current Exponential 6.0 line: from an earlier 6.0.x, from 5.4 or 5.90, or from the 4.x and older 5.x lines. It explains how to find the version you run, the one rule that keeps an upgrade safe (back up, then never run the installer over an existing site), the database update files in update/database/ for each engine and how to apply them, the data repair scripts in update/common/scripts/, the console commands that check the result, and the release notes to read for each period you pass. It summarises and extends the Upgrading guide, which remains the step-by-step procedure.

Warning

.htaccess slows down every request. When .htaccess files are enabled (AllowOverride anything but None), let alone used, Apache looks for them and reads and evaluates them on every request, in every directory of the path, and page loading times go up drastically (measured). The only real fix is to switch the feature off for the virtual host: AllowOverride None in the HTTPS virtual host, and the rules in the virtual host's own configuration as mod_rewrite rules. That is far faster in production speed tests and well worth the time it takes to optimize a production configuration. Switch over right away: copy the rules of the example HTTPS virtual host into your virtual host (with the static cache, also staticcache_offload.conf), set AllowOverride None, run apachectl configtest, reload Apache and test the site. The same rules for other servers: nginx, LiteSpeed, Caddy, IIS, Velocity (built in); where virtual host rules go in a control panel: Plesk, cPanel, DirectAdmin, ISPConfig. On LiteSpeed or OpenLiteSpeed, the server's own page cache can also answer anonymous visitors and crawlers without PHP: Page caching with LiteSpeed and OpenLiteSpeed.

Contents · Previous: 10. After installing · Next: 12. Troubleshooting

11.1 Before you start

Never run an installer over an existing site. The setup wizard, the kickstarter and exp:install create a new database; with DatabaseAction=remove (the default of exp:install) they empty the one they are pointed at. An upgrade replaces the code and updates the existing database in place. exp:install refuses to run where settings/override/site.ini.append.php already names a database ("This directory already holds an installation"), and the kickstarter refuses to run its CreateSites step without --force ("The CreateSites step will modify the database and site settings."), whatever the directory holds. Do not add --force to get past either on a live site.

Who runs the commands. The commands in this chapter are written as the site's own user runs them. Run as root, the scripts refuse to start unless --allow-root-user is added, and files they create in var/ belong to root afterwards, which the web server cannot overwrite (chapter 12). Prefer sudo -u <site user> php ....

Back up first, every time. The database step is the one that cannot be undone by putting files back.

mysqldump -u USER -p DATABASE > backup-before-upgrade.sql          # MySQL or MariaDB
pg_dump -U USER DATABASE > backup-before-upgrade.sql               # PostgreSQL
sqlite3 var/storage/sqlite3/exponential.db ".backup backup-before-upgrade.db"   # SQLite, online backup
tar czf backup-var-and-settings.tgz var settings/override settings/siteaccess extension design

For SQLite use the file name your installation has ([DatabaseSettings] Database in settings/override/site.ini.append.php); .backup is SQLite's online backup and is safe while the site runs, a plain cp of a busy database file is not. Restore the backup on a scratch machine once: it is the only proof that it works.

Take the site offline for the database step.

php bin/php/maintenance.php on --message="Upgrade in progress" --until=30m
php bin/php/maintenance.php status
php bin/php/maintenance.php off

While maintenance mode is on, every page request is answered with the maintenance page (HTTP 503, never cached); images, styles and scripts are still served. --allow-ip= and --allow-admin let you in to check. See Maintenance mode.

11.2 Which version do you run?

The code and the database each carry a version, and after a correct upgrade they agree.

WhereHowExample
The codephp bin/php/console --version (reads lib/version.php)console (Exponential) 6.0.15stable
The databasethe rows ezpublish-version and ezpublish-release of table ezsite_data6.0.14, 1
The adminSetup > System information
mysql -u USER -p DATABASE -e "SELECT name, value FROM ezsite_data WHERE name LIKE 'ezpublish-%';"
psql -U USER DATABASE -c "SELECT name, value FROM ezsite_data WHERE name LIKE 'ezpublish-%';"
sqlite3 var/storage/sqlite3/exponential.db "SELECT name, value FROM ezsite_data WHERE name LIKE 'ezpublish-%';"

The row names keep their historic spelling on purpose; they are identifiers, and every update file writes to them.

On a site that is current, the query prints (MySQL's layout):

+--------------------+--------------+
| name               | value        |
+--------------------+--------------+
| ezpublish-release  | 1            |
| ezpublish-version  | 6.0.15stable |
+--------------------+--------------+

The 6.0.0 to 6.0.15 file writes 6.0.15stable; the earlier files write a number such as 6.0.0 (the 5.3 to 5.4 file writes 5.4.0alpha1, as it did upstream). A version row that does not match the code means an update file is still missing, and the code may already use a column the database does not have yet: find the step from the table below and apply what is missing before the site is used.

You runPathSection
3.10 up to 5.3the old chain to 5.4, then to 6.0.0, then to today11.4, 11.5, 11.6
5.4.0, or 5.90 (2017.08 and later)to 6.0.0, then to today11.5, 11.6
6.0.0 to 6.0.14, or an earlier checkout of the 6.0.15 lineto today11.6

This chapter is the update chain itself. For the practical side of moving a whole site, the migration chapters go further: 14. from the 4.x line, 15. from the 5.x legacy stack, 16. from eZ Platform and Ibexa, and the lookup tables in 17. Migration reference.

11.3 The update files

update/database/<engine>/<version>/ holds one SQL file per step. Each file takes a database from one version to the next, and the chain must be walked in order, every file once.

By engine

EngineDirectorySteps available
MySQL or MariaDBupdate/database/mysql/3.10 → 4.0 → 4.1 → ... → 5.4, then 6.0/dbupdate-5.4.0-6.0.0.sql, then 6.0/dbupdate-6.0.0-6.0.15.sql
PostgreSQLupdate/database/postgresql/the same chain; the 5.4 step is 6.0/dbupdate-5.4-to-6.0.sql, then 6.0/dbupdate-6.0.0-6.0.15.sql
SQLiteupdate/database/sqlite/only 6.0/dbupdate-6.0.0-6.0.15.sql: SQLite support starts with 6.0.1
Oracleextension/ezoracle/update/database/ (in the ezoracle extension): ezpublish/ for the kernel chain, ezoracle/ for the driver's own tablesthe kernel chain up to 5.3
MongoDBnonethe MongoDB driver arrived in 6.0.14; the audit tables of 6.0.15 are created by createaudittables.php (below); the trash fields trashed_by and trashed_via need no update (trash)

The 4.0 to 5.4 chain (MySQL file names; PostgreSQL has the same names)

FromToFile
3.104.04.0/dbupdate-3.10.0-to-4.0.0.sql
4.04.14.1/dbupdate-4.0.0-to-4.1.0.sql
4.14.24.2/dbupdate-4.1.0-to-4.2.0.sql
4.24.34.3/dbupdate-4.2.0-to-4.3.0.sql (+ dbupdate-cluster-4.2.0-to-4.3.0.sql on MySQL)
4.34.44.4/dbupdate-4.3.0-to-4.4.0.sql
4.44.54.5/dbupdate-4.4.0-to-4.5.0.sql
4.54.64.6/dbupdate-4.5.0-to-4.6.0.sql
4.64.74.7/dbupdate-4.6.0-to-4.7.0.sql (+ dbupdate-cluster-4.6.0-to-4.7.0.sql on MySQL)
4.75.05.0/dbupdate-4.7.0-to-5.0.0.sql
5.05.15.1/dbupdate-5.0.0-to-5.1.0.sql
5.15.25.2/dbupdate-5.1.0-to-5.2.0.sql (+ dbupdate-cluster-5.1.0-to-5.2.0.sql on MySQL)
5.25.35.3/dbupdate-5.2.0-to-5.3.0.sql
5.35.45.4/dbupdate-5.3.0-to-5.4.0.sql (+ dbupdate-cluster-5.3.0-to-5.4.0.sql on MySQL)

Rules

  • In order, each once. If a statement fails because a column or table already exists, the file was applied before: stop and compare with the backup.
  • Cluster files. If the installation stores files in the database cluster (DFS or the database file handler), also apply the dbupdate-cluster-... file of the same step (MySQL: 4.3, 4.7, 5.2, 5.4).
  • unstable/ directories hold the files of pre-releases (alpha, beta, rc). Do not apply them to a site that ran a final release.
  • 6.12/, 7.2/ and 7.3/ are not steps of this chain; never apply them as files. Their schema changes are in the 6.0 files: see below.
  • A file that fails half way. Restore the backup and apply the file again from the start; do not repair by hand and re-run.
  • Check the chain itself. php bin/php/console exp:checkdbfiles --no-verify-branches verifies the update files against the upgrade path, every directory from 4.0 to 7.3 on every engine, and reports missing (!) or stray (?) files. Without --no-verify-branches it also tries to compare 4.3 to 5.4 with the retired SVN repository of the old vendor, which fails with one C line.

Apply a file with the client of the database:

mysql -u USER -p DATABASE < update/database/mysql/4.1/dbupdate-4.0.0-to-4.1.0.sql
psql -U USER -d DATABASE -f update/database/postgresql/4.1/dbupdate-4.0.0-to-4.1.0.sql
sqlite3 var/storage/sqlite3/exponential.db < update/database/sqlite/6.0/dbupdate-6.0.0-6.0.15.sql

The 6.12, 7.2 and 7.3 directories

These three directories come from the upstream legacy kernel of 2016 to 2018, the code Exponential 6.0 is built on. In those years its update files were numbered after the version of the Symfony kernel the legacy kernel ran beside, not after a legacy release, and each file records that number in ezsite_data. Exponential kept them unchanged apart from the SET default_storage_engine line. They are not steps between 5.4 and 6.0.

FileEnginesFromWhat it does
6.12/dbupdate-5.4.0-to-6.12.0.sqlMySQL, PostgreSQLFebruary 2016, password security (EZP-24744)records 6.12.0; widens ezuser.password_hash to 255
7.2/dbupdate-6.13.0-to-7.2.0.sqlPostgreSQLJune 2018, sequence names (EZP-28706)records 7.2.0; renames every sequence from <table>_s to <table>_<column>_seq and points each column default at the new name
7.3/dbupdate-7.2.0-to-7.3.0.sqlMySQL, PostgreSQLOctober 2018, trash date (EZP-28881)records 7.3.0; adds ezcontentobject_trash.trashed

Do not apply them as files to a database on the Exponential path. Each would replace the version row with a number that is not an Exponential version, and 7.2/ starts from 6.13.0, a version no Exponential database has.

Their schema changes are in the 6.0 files. The 6.0 kernel was built after all three changes and uses their schema; since October 2026 the 6.0 files give a database that comes from 5.4 (or 5.90) all of them, so nothing needs to be run by hand:

  • ezuser.password_hash widened to 255 (11.5, step 2).
  • ezcontentobject_trash.trashed, without which moving content to the trash fails: MySQL and PostgreSQL, same definition as the 7.3/ file, added only when the column is missing.
  • PostgreSQL only, the sequence names: the 6.0 kernel reads the last inserted id from <table>_<column>_seq, so on a database that still has the <table>_s sequences of 5.4 every insert that needs its new id fails. The same 88 sequences as the 7.2/ file are renamed, each only when the old name exists and the new one is free, and each column default is pointed at the new name when it does not name it yet.

All three are in the 5.4 to 6.0.0 file and, for a site that reached 6.0 with an older copy of it, again in the 6.0.0 to 6.0.15 file. On a database that already has a change they leave it as it is, so they are harmless on a database installed by Exponential 6.0 or one that ran those upstream releases (its version row reads 6.12.0, 6.13.0, 7.2.0 or 7.3.0); such a database continues with 11.6. SQLite needs none of them: its schema has always had the column and it has no sequences.

Per-database notes

EngineNote
MySQL or MariaDB6.0/dbupdate-5.4.0-6.0.0.sql, like 6.12/, 7.3/ and the old chain's files, starts with SET default_storage_engine=InnoDB;, which MySQL (from 5.5.3) and MariaDB accept: apply the files whole. Copies from before October 2026 started with SET storage_engine=InnoDB;, a spelling MySQL removed in 5.7.5 and MariaDB in 12.0; take the current files. 6.0/dbupdate-6.0.0-6.0.15.sql has no such line, because each of its tables names its engine.
MySQL or MariaDBTables must be UTF-8; php bin/php/ezconvertdbcharset.php converts an old database. php bin/php/ezconvertmysqltabletype.php --list lists the table types and --newtype=InnoDB converts them.
PostgreSQLThe 5.4 step is 6.0/dbupdate-5.4-to-6.0.sql: the version rows, the wider password_hash, the trashed column and the sequence renames (above). Its guarded steps are DO blocks, which need PostgreSQL 9.0 or newer. The digest function of pgcrypto must exist in the database.
SQLiteEach ALTER TABLE adds one column, because SQLite takes only one per statement, and SQLite has no ADD COLUMN IF NOT EXISTS: on a second run, every ALTER TABLE for a column that is already there fails ("duplicate column name"). Run the file once, after SQLite's online backup.
OracleThe ezoracle extension carries its own update files up to 5.3; apply them with your Oracle client. The 6.0.15 audit tables come from createaudittables.php.
MongoDBNo SQL files. createaudittables.php creates the audit index collections through the driver's schema handler. The trash documents get trashed_by and trashed_via when written, and an older one reads as their defaults, so movetrashrecords.php runs as on the other engines.

11.4 From 3.10 to 5.3: the old chain

  1. Put the new code in place (as in 11.6, step 1).
  2. Apply the update files of the chain in 11.3, from your version up to 5.4, each once.
  3. Run the data repair scripts of every version you pass (below), from the installation root.
  4. Read the release notes of each version you pass: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 5.0, 5.1, 5.2, 5.3, 5.4.
  5. Read the notes of the 5.90 line, which prepared the move to PHP 7 and a longer password rule: 5.90 overview, PHP 7, password length, relation indexing.
  6. Continue with 11.5.

The data repair scripts

update/common/scripts/<version>/ holds PHP scripts that repair or convert data for a step. Run the ones of the versions you pass, from the installation root, after that version's SQL file, for example:

php update/common/scripts/4.1/updateimagesystem.php --help
php update/common/scripts/4.1/updateimagesystem.php

Read the first lines of a script, or its --help, before you run it. The scripts and what they say they do:

VersionScriptPurpose
4.0updatebinaryfile.php, updatetipafriendpolicy.php, updatevatcountries.phpbinary file attributes, the tip-a-friend policy, VAT countries
4.1addlockstategroup.php, correctxmlalign.php, fixclassremoteid.php, fixezurlobjectlinks.php, fixnoderemoteid.php, fixobjectremoteid.php, initurlaliasmlid.php, updateimagesystem.phpobject states, XML alignment, remote ids, URL links, multi-language URL aliases, the image system
4.2fixorphanimages.phporphaned image files
4.3updatenodeassignment.phpnode assignments
4.4, 4.5updatesectionidentifier.phpsection identifiers
4.6removetrashedimages.php, updateordernumber.phpimages of trashed content, web shop order numbers
5.0deduplicatecontentstategrouplanguage.php, disablesuspicioususers.php, restorexmlrelations.phpduplicate state group translations, disabling accounts whose login contains < or >, XML relations
5.1fiximagesoutsidevardir.phpreferences to images outside VarDir
5.2cleanupdfscache.phpcache records in the DFS storage table
5.3recreateimagesreferences.php, updatenodeassignmentparentremoteids.phpmissing image references, parent_remote_id of node assignments
5.4cleanuntranslatablerelations.php, cleanupfieldvaluerelations.php, fixremovedezurlobjectlinks.php, fixtrashedimagereferences.phpstale relations, links and image references
6.0createaudittables.php, movetrashrecords.phpthe audit index tables of 6.0.15 (see 11.6), who trashed what from trashed.json into the trash rows
anycleanup.php, updatecontentobjectname.php, updatenbxmlcontents.phpgeneral clean-up, object names, non-breaking space encoding in XML content

Coming from a 5.x site on the Symfony stack

A 5.x site that ran the legacy kernel inside a Symfony project kept it in ezpublish_legacy/; the installer plugin still installs it there when the project's composer.json sets extra.symfony-app-dir. The database and the legacy settings, extensions and designs are those of the legacy installation, and the path above applies to them. To keep running Exponential 6 inside a Symfony platform, see Legacy bridge and the legacy bridge bundle specification.

11.5 From 5.4 (or 5.90) to 6.0.0

  1. New code. Either a fresh checkout or composer create-project of the new version, into which you copy your settings/override/, settings/siteaccess/, your own extension/ directories and your own design/ directories; or, when the site is a Composer project, change the constraint of se7enxweb/exponential in your composer.json and run composer update se7enxweb/exponential. Never let a tool replace your own settings or extensions.

  2. Record the version and bring the schema to what the 6.0 kernel uses. The file records 6.0.0 and makes three schema changes a 5.4 database needs (11.3): it widens ezuser.password_hash from the varchar(50) of every 5.x schema to 255, the width of the kernel schema; it adds ezcontentobject_trash.trashed; and on PostgreSQL it renames the sequences to <table>_<column>_seq:

    mysql -u USER -p DATABASE < update/database/mysql/6.0/dbupdate-5.4.0-6.0.0.sql
    psql -U USER -d DATABASE -f update/database/postgresql/6.0/dbupdate-5.4-to-6.0.sql

    Apply the whole file: none of it is optional. With the default [UserSettings] HashType=php_default and UpdateHash=true every user's hash is rewritten at the first sign-in to a 60-character bcrypt hash, which a 50-character column refuses or cuts, and the user cannot sign in again; without trashed moving content to the trash fails; with the old sequence names every insert that needs its new id fails on PostgreSQL. Each change leaves a database that already has it as it is, so the file can run again. Copies of the file from before October 2026 only recorded the version; the 6.0.0 to 6.0.15 file of 11.6 makes the same changes, for a site that used one. Oracle (ezoracle) has no such file: run ALTER TABLE ezuser MODIFY ( password_hash VARCHAR2(255) ); with your Oracle client, and add trashed (ALTER TABLE ezcontentobject_trash ADD ( trashed INTEGER DEFAULT 0 NOT NULL );) if the table lacks it. The MySQL file runs on every MySQL from 5.5.3 and on MariaDB (see the per-database notes above). Details: Changelog 6.0.0.

  3. PHP. The current line runs on PHP 8.0 to 8.5 (composer.json: ^8.0), Velocity included from its release 0.0.4.54. Releases 6.0.8 to 6.0.14 required 8.1. A site on PHP 7.4 can run the current line again from 6.0.15, without support (6.0.8 to 6.0.14 need PHP 8.1). Check your own extensions for PHP 8 problems: classes that extend eZPersistentObject or eZDataType are the usual cases. See PHP 8 support and PHP 8.0 support.

  4. Continue with 11.6.

6.0.8 to 6.0.14 asked for 8.1 in composer.json; from 6.0.15 it accepts 8.0 again, so a server whose operating system ships PHP 8.0 can take the current line under Apache, nginx or Velocity (Velocity from its release 0.0.4.54) (PHP 8.0 support). Composer enforces the range: on an older PHP, composer install stops with "Your requirements could not be resolved to an installable set of packages" and names the PHP version, before anything is changed.

11.6 From any 6.0.x to today

Step 1: put the new code in place

As in 11.5 step 1: a checkout of the tag or branch (git fetch --tags, git checkout v6.0.<n> or main) followed by composer install, or composer update se7enxweb/exponential in a Composer project. Then regenerate the class maps and clear the caches, in this order:

php bin/php/ezpgenerateautoloads.php -e
php bin/php/ezcache.php --clear-all

Why the autoloads first: a new release can add or rename classes, and a class missing from the arrays is "not found" even though its file is there. Why the caches next: INI, template and override caches hold what the old code compiled.

The generator stops with exit status 1 on an option it does not know, so a mistyped option in an upgrade script is noticed. When you regenerate the kernel array (-k), the directories in .autoloadignore are left out; the shipped file excludes vendor, var and the .claude worktree folder. An installation that keeps other working copies of the code inside its root (for example Git worktree folders) must add them there or pass --exclude='<folder>'; otherwise kernel classes are mapped into those copies.

Then reload the PHP that serves the site: the PHP-FPM service of the site (systemctl reload <service>; on Plesk plesk-php<XY>-fpm, not php-fpm) and, under Velocity, the server:

php bin/php/console exp:velocity deploy --dry-run
php bin/php/console exp:velocity deploy

exp:velocity deploy runs every step in the right order: the extension autoloads, the INI cache, the template, override, translation and design caches, the engine archive when needed, the PHP-FPM reload ([DeploySettings] PhpFpmService), the Velocity restart, then the content, HTTP and template-block caches and Velocity's response cache. It prints PASS, FAIL or SKIP with the time of each and stops at the first failure. Add --kernel when the upgrade added or renamed kernel classes (the release notes say so). The order matters: a cache of rendered pages cleared before PHP runs the new code fills up again with pages of the old code. See chapter 8 and Velocity engines.

Compare your files with the new templates. A release can change .htaccess_root, .htaccess_root_static and config.php-RECOMMENDED; your .htaccess and config.php stay as they are:

php bin/php/console exp:setup:status                     # is .htaccess still a copy of a template?
php bin/php/console exp:setup:htaccess                   # the diff between your .htaccess and the new template
php bin/php/console exp:setup:htaccess --force --apply   # take the new rules (backup first), when the diff holds nothing of yours
php bin/php/console exp:config:list                      # settings new in config.php-RECOMMENDED show up as (not set)

Set the new settings you want with exp:config:set <NAME> <value> --apply; the settings you had keep their values. See Setting up .htaccess and config.php from the console.

Step 2: apply the database update

The file for the line is update/database/<engine>/6.0/dbupdate-6.0.0-6.0.15.sql, for MySQL, PostgreSQL and SQLite. It records the version (6.0.15stable); on MySQL and PostgreSQL it makes the three changes of the 5.4 to 6.0.0 file (the wider ezuser.password_hash, ezcontentobject_trash.trashed and, on PostgreSQL, the <table>_<column>_seq sequence names) for a site that came from 5.x with a copy of that file from before October 2026, and leaves a database that already has them as it is (SQLite needs none of them: 11.3); and it adds the tables and columns of the line: the PDF export footer (ezpdf_export.show_footer, footer_text), OPML and podcast exports (ezrss_export.opml_head, podcast_head, table ezrss_export_opml_item), the audit index (expaudit_cursor, expaudit_event, expaudit_file), bookmark folders (expbookmark_folder, ezcontentbrowsebookmark.folder_id, priority), the e-mail preferences (expmail_category, expmail_consent_log, expmail_pending, expmail_preference, expmail_suppression) and who moved an object to the trash (ezcontentobject_trash.trashed_by, trashed_via; until they exist the kernel keeps writing <VarDir>/trash/trashed.json, afterwards run php update/common/scripts/6.0/movetrashrecords.php, see the trash view).

Apply it once, whole, in order. If the database already has a column, the statement fails and names it; skip only that statement.

A site that came from 5.x and already applied an older copy of this file may still lack those three changes (SHOW COLUMNS FROM ezuser LIKE 'password_hash' and SHOW COLUMNS FROM ezcontentobject_trash LIKE 'trashed'; on PostgreSQL \d ezuser, \d ezcontentobject_trash and \ds in psql). Do not apply the file again: apply the current 5.4 to 6.0.0 file of your engine, which makes them and can run again, and then set the version row, which that file sets to 6.0.0, back: UPDATE ezsite_data SET value='6.0.15stable' WHERE name='ezpublish-version';.

mysql -u USER -p DATABASE < update/database/mysql/6.0/dbupdate-6.0.0-6.0.15.sql

Then create the audit index tables and index the audit files written so far (the audit trail is on by default from 6.0.15):

php update/common/scripts/6.0/createaudittables.php --dry-run
php update/common/scripts/6.0/createaudittables.php

The script works on every engine (MySQL/MariaDB, PostgreSQL, SQLite, Oracle and MongoDB, each through its schema handler) and leaves existing tables alone, so it can be run again. --dry-run shows which tables are missing; --no-index creates the tables only. If it stops with "The audit index classes are not in the autoload array", run php bin/php/ezpgenerateautoloads.php -k first. See Audit.

Check the version row afterwards (the query in 11.2); expected 6.0.15stable.

Step 3: read the checklist of each period you pass

Read every release note between your version and today; each opens with what to check.

FromRead
6.0.06.0.1: SQLite, replacing kernel modules from an extension
6.0.16.0.2: links in the online editor
6.0.26.0.3: datatype extensions joined the distribution
6.0.36.0.4: the responsive administration design
6.0.46.0.5: a failed 6.0.4 install, datatypes of extensions
6.0.56.0.6: admin3 on phones and tablets
6.0.66.0.7: the last release for PHP 7.4 until 6.0.15
6.0.76.0.8: needs PHP 8.1; and PHP 8 support
6.0.86.0.9: PostgreSQL, REST access to content
6.0.96.0.10: the product name in templates, translations and overrides; The product is called Exponential
6.0.106.0.11: PHP 8.5, notification mail, page view counting
6.0.116.0.12: role-based template operators, several sites in one installation
6.0.126.0.13: security fixes, sort column checks; Security hardening 6.0.13, Hardening
6.0.136.0.14: MongoDB, the upgrade file check
6.0.146.0.15, then in order: July and August 2026, 16 to 30 September 2026 with Security defaults, 1 and 2 October 2026, e-mail preferences and the mail gate

The changes people meet most often when they reach 6.0.15:

  • Accounts without an ezuser_setting row cannot sign in (16 August 2026): a user with no settings row used to count as enabled and now counts as disabled. Accounts made in the admin always have the row; accounts made by SQL, packages or migrations may not. The queries that find them are in the August 2026 security specification.
  • Overrides follow Priority (12 July 2026): override blocks of override.ini are sorted by Priority, lowest first, and a block without it comes last. Review any override.ini where only some blocks have a Priority.
  • Security headers, HttpOnly/SameSite session cookies and a stricter upload check are on by default (September and October 2026). [Session] CookieSecure=auto marks the session cookie Secure whenever the request came over HTTPS. If another host frames your admin, set the header deliberately.
  • Legacy extensions were brought to one standard between 22 September and 2 October 2026: Behaviour changes of the legacy extensions.
  • Static cache defaults changed: Static cache defaults.
  • Static publishing is new (November 2026): off until you switch it on; the upgrade step 6.0.15-static-publish-tables makes the tables exp_static_run and exp_static_page. Static publishing.
  • Velocity, if you run it: Velocity engine upgrade notes and on-disk layout.

For the story by month, see the chronicle; for every single change, the ledger of the root repository.

Step 4: check your own extensions

php bin/php/ezpgenerateautoloads.php -e
php bin/php/console exp:checkclasses

exp:checkclasses loads every declared class and reports those PHP refuses to load, which is how a PHP 8 incompatibility in an extension shows. Extensions carry their own version in ezinfo.php and extension.xml, and the admin's Setup > Extensions list shows it (Extension list). The order in which extensions load changed in 2026: Extension loading order. Composer-installed extensions are updated with their package: composer update se7enxweb/<package>, after reading the package's notes in the extensions' release notes.

Some classic extensions have their own upgrade scripts in bin/php/:

ScriptUpgrades
php bin/php/ezwebinupgrade.php --to-version=<version>an existing ezwebin installation to the current package version
php bin/php/ezflowupgrade.php --to-version=<version>the installed ezflow packages (--package, --url)

Run them with --help first.

11.7 Verify the upgrade

php bin/php/ezcache.php --clear-all                                # ends without an error
php bin/php/console exp:checkdbfiles --no-verify-branches          # prints nothing: every update file is in place
php bin/php/console exp:checkclasses                               # no class PHP refuses to load
php bin/php/console exp:checkmanifest --all                        # the installed files against share/filelist.md5; exit status 0 when all match
php bin/php/console exp:expfetch                                   # the new version, PHP, engine and database at a glance; no error hint

exp:expfetch (the system card) shows the version you upgraded to, the PHP branch and its support date, the engine and the database in one screen, and ends with hints for what needs attention (OPcache, a missing PHP extension, a stale help index, cron). Keep ./console exp:info --json --no-logo from before and after the upgrade and compare them to see every extension version that changed.

exp:checkdbfiles reports only problems, one line each: ! for a file the upgrade path names but the disk lacks, ? for a file on disk the path does not know. No output (apart from the console's own running ... line) means the update files are complete. Without --no-verify-branches it also tries to reach the retired SVN repository of the old vendor and prints a C line for that; ignore it or add the option.

Then in the browser: the front page loads; you can log in to the admin; Setup > System information shows the new version; a test item can be published and appears on the site. Setup > Upgrade check with Check file consistency compares the installed files with the file manifest share/filelist.md5 and lists the ones that differ (File consistency check). Files you changed on purpose are expected there.

Other tools that help after an upgrade:

CommandWhen
php bin/php/ezsqldiff.php --type=mysql --user=USER --password=PASS share/db_schema.dba <database>compare your database with the reference schema, which goes first. The output is the SQL that would turn the second argument (your database) into the first (the reference): a CREATE TABLE or ADD line names something an update step should have made, a DROP TABLE line a table only your database has (your own extensions' tables appear that way). Read it, do not run it. Exit code 0 when the schemas match, 1 when they differ (--type is mysql or postgresql). Chapters 14 to 16 use it the same way
php bin/php/updateniceurls.phprebuild the URL aliases (--update-nodes)
php bin/php/updatesearchindex.phpreindex all content in the search engine
php bin/php/cleanupversions.php --applyremove archived versions beyond the VersionManagement limits

Last, clear the content cache and, under Velocity, its response cache, after the PHP-FPM reload or Velocity restart, so that a page rendered by the old code is not served again:

php bin/php/ezcache.php --clear-tag=content
php bin/php/console exp:velocity cache clear --apply

11.8 If something goes wrong

SymptomWhat to do
A white page or HTTP 500 after the code changeRead var/log/error.log; run step 1 of 11.6 again; vendor/ must match the new composer.json.
"The site is missing the software libraries it needs" (HTTP 503)vendor/ is missing: composer install; see Repairing an installation.
Class ... not foundphp bin/php/ezpgenerateautoloads.php -e, then clear the caches.
An SQL file failed half wayRestore the backup and apply the file again from the start.
SET storage_engine=InnoDB; failsAn update file from before October 2026: the current files say SET default_storage_engine=InnoDB;. Take the current file, or skip that one line.
Someone cannot sign inThe ezuser_setting change of August 2026 (11.6, step 3).
A change does not show under Velocityexp:velocity restart (or deploy), then exp:velocity cache clear.

More in chapter 12.

A published release never changes; a fix always comes as the next version. To see what a version holds, open its changelog: 6.0.0 to 6.0.15.

Demo mode (6.0.15). Nothing changes on an upgraded site unless the environment variable EXP_DEMO_MODE=1 is set, which is only for public demos. After upgrading a production server, check that no process of a real site has it (grep -r EXP_DEMO_MODE over the PHP-FPM pools, virtual hosts, .htaccess files, service units and crontabs). Demo mode.

Icon themes of extensions (6.0.15). The shipped rewrite rules now serve extension/<name>/icons/ (SVG, PNG and GIF images whose name has exactly one extension) as files. An upgrade never rewrites your own .htaccess or server configuration: if an extension of the site ships icons (icon.ini [ExtensionSettings] IconExtensions[]), copy the rule into it, before the front controller rule. Apache .htaccess: RewriteRule ^extension/[^/]+/icons/[A-Za-z0-9_/-]+\.(svg|png|gif)$ - [L]; a virtual host takes ^/extension/..., nginx location ~ ^/extension/[^/]+/icons/[A-Za-z0-9_/-]+\.(svg|png|gif)$ { try_files $uri =404; }. Velocity and the generated FrankenPHP configuration have it after exp:velocity deploy. Every server: Web server rules.

References

In this repository:

External:

Contents · Previous: 10. After installing · Next: 12. Troubleshooting

Become an Exponential Partner

Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x