Joomla Update Servers and the Joomla Extensions Directory
|
This content was generated with the assistance of AI and should be verified against the official Joomla documentation before being relied on in production. This sub-section’s Joomla bibliography lists the reference material consulted while preparing these pages. |
An extension that is installed on hundreds of sites is only as safe as its update path. Joomla does not push updates: each site periodically downloads small XML files from the update servers that the extension’s manifest names, compares them with what is installed and offers the newest suitable release. The XML is therefore the contract between the developer and every site, and the Joomla Extensions Directory (JED) is the public catalogue that points users to extensions that follow the rules of that contract. This page is the developer’s reference for both.
It was checked on 2026-10-30 against Joomla 6.2.x and PHP 8.3 or later, using the Joomla Developer Manual, the updater classes of the 6.2 source tree (libraries/src/Updater, libraries/src/Installer/InstallerHelper.php and administrator/components/com_installer) and the public JED site. The running example is the Tallpine Hiking Club package pkg_tallpine with its component com_tallpine (namespace Tallpine\Component\Tallpine). The administrator’s side (the Update Sites screen, Rebuild, the cache time and the minimum stability) is covered in Extensions: installing, updating and update sites; the manifest elements are in Extension types, manifests and packaging; building and publishing from a pipeline is in Build and CI for extensions. This page goes into the update XML itself and what the updater does with it.
The update flow end to end
When the administrator opens System → Update → Extensions (or the scheduled check runs), the updater loads every enabled row of #__update_sites. A site of type collection yields more locations; a site of type extension yields update entries. The entries that pass the filters described below are reduced to the single highest version per extension and stored in #__updates. Selecting Update re-reads the XML, downloads the zip, verifies the checksum and hands the file to the installer, which runs the adapter’s update path, the SQL update files and the install script (see Installer scripts and SQL updates).
Extension update sites are plain XML over HTTPS. The The Update Framework (TUF) metadata that protects core downloads since Joomla 5.1 is a separate mechanism for the core update site: in the 6.2 source, UpdateModel refuses an extension update site of type tuf with the message JLIB_INSTALLER_TUF_NOT_AVAILABLE, so third-party extensions cannot rely on it. Their integrity guarantees are HTTPS and the checksum elements, discussed under signing below.
Declaring the update server
The manifest of the extension (or, better, of the package) declares where the XML lives:
<!-- pkg_tallpine.xml (excerpt) -->
<extension type="package" method="upgrade">
<name>PKG_TALLPINE</name>
<packagename>tallpine</packagename>
<version>1.4.0</version>
<changelogurl>https://updates.tallpine.example/changelog.xml</changelogurl>
<updateservers>
<server type="extension" priority="1" name="Tallpine Hiking Club">https://updates.tallpine.example/pkg_tallpine.xml</server>
</updateservers>
</extension>
Source: Joomla Manual: Update Servers
Declare the server once, on the package, rather than on each child extension: a package whose children also declare servers produces several update sites and several prompts for one product. The URL must have no spaces or line breaks around it. The name is shown in the Update Sites list as written, so do not use a language key. type="collection" points to an extensionset file instead (used by language packs and by vendors with many products):
<?xml version="1.0" encoding="utf-8"?>
<extensionset name="Tallpine Hiking Club extensions" description="All Tallpine extensions">
<extension name="Tallpine package" element="pkg_tallpine" type="package" version="1.4.1"
detailsurl="https://updates.tallpine.example/pkg_tallpine.xml"/>
<extension name="Next hikes module" element="mod_tallpine_nexthikes" type="module" client="site" version="1.2.0"
detailsurl="https://updates.tallpine.example/mod_tallpine_nexthikes.xml"/>
</extensionset>
Source: Joomla Manual: Update Servers
Each extension entry needs name, element, type, version and detailsurl, which points at that extension’s own update XML.
The update XML
A complete update file for the Tallpine package, with every field a careful developer sets:
<?xml version="1.0" encoding="utf-8"?>
<updates>
<update>
<name>Tallpine Hiking Club package</name>
<description>Trail reports, conditions and next-hikes module.</description>
<element>pkg_tallpine</element>
<type>package</type>
<version>1.4.1</version>
<infourl title="Tallpine 1.4.1">https://updates.tallpine.example/releases/1.4.1.html</infourl>
<changelogurl>https://updates.tallpine.example/changelog.xml</changelogurl>
<downloads>
<downloadurl type="full" format="zip">https://updates.tallpine.example/dl/pkg_tallpine-1.4.1.zip</downloadurl>
<downloadsource type="full" format="zip">https://mirror.tallpine.example/dl/pkg_tallpine-1.4.1.zip</downloadsource>
</downloads>
<security>0</security>
<tags>
<tag>stable</tag>
</tags>
<maintainer>Tallpine web team</maintainer>
<maintainerurl>https://tallpine.example</maintainerurl>
<targetplatform name="joomla" version="(5\.[4-9]|6\.[0-9]+)"/>
<php_minimum>8.3</php_minimum>
<supported_databases mysql="8.0.0" mariadb="10.6.0" postgresql="14.0"/>
<sha512>CHANGE_ME_SHA512_OF_THE_ZIP</sha512>
</update>
</updates>
Source: Joomla Manual: Update Servers
Identity elements
name is shown in the Update view. element must equal the installed extension’s element (pkg_tallpine, com_tallpine, mod_tallpine_nexthikes; for a plugin, its main file name such as tallpine_conditions) and type the extension type. A plugin additionally needs folder (content, system, …), and modules and templates need client (site or administrator); for plugins and front-end modules the client defaults to site, so state it in the entry. In the updater classes the client text is converted to the numeric client id with ApplicationHelper::getClientInfo(), and a numeric value is still accepted for backwards compatibility. If any of these identity fields differs from the installed row, the update is never matched and the site simply shows nothing.
version is compared with PHP’s version_compare(), so 1.4.10 is newer than 1.4.9, 1.5.0-rc1 is older than 1.5.0 and an arbitrary suffix has surprising ordering. Keep to major.minor.patch.
targetplatform
targetplatform is the only mandatory filter. name must be joomla (it is compared with the lowercased product name) and version is a regular expression. The 6.2 updater evaluates it as preg_match('/^' . $version . '/', JVERSION): it is anchored at the start but not at the end. Three consequences follow.
-
5.4also matches5.40, and an unescaped dot matches any character. Escape dots and group alternatives:(5\.[4-9]|6\.)` covers 5.4 and later 5.x and every 6.x, and `6\.[0-9]alone targets 6.x only. -
The pattern must not contain the character
/, because it is also the regex delimiter. -
The Manual documents the optional attributes
min_dev_levelandmax_dev_level, which filter on the third number of the version. The updater classes of the 6.2 source tree do not read them, so a patch-level limit has to be written into the pattern itself, for example6\.2\.[0-4]$.
Because the file may hold many update entries, a developer who supports several Joomla series publishes one entry per release and series, each with the matching targetplatform, php_minimum and download. The updater keeps the highest version among the entries that pass every filter. Older books such as Developing Extensions for Joomla! 5 (2023) show a pattern that stops at version 5.0 together with Joomla 4-era database minimums; because the match is a regular expression against the running version, that file hides every update from Joomla 5.1 onward and from Joomla 6. Review the pattern at every Joomla minor release, and test it (see the checking section below).
php_minimum and supported_databases
php_minimum is compared with PHP_VERSION using >=. If the site’s PHP is older, the update is not offered for installation and the administrator sees a warning that the version exists but needs a newer PHP. supported_databases carries one attribute per server type (mysql, mariadb, postgresql; the Manual also lists mssql, which core has not supported since 4.0) with a minimum version. The updater looks up the running server type: MariaDB is detected through the version string and its leading 5.5.5- prefix is stripped. If the server type has no attribute at all, the update is also blocked with a message that the database type is not supported, so list every database you really support. When the element is absent, any database is assumed to match. The core minimums of Joomla 6 (the Manual and the upgrade guide disagree on the MariaDB and PostgreSQL floor: 10.4 and 12 in one, 10.6 and 14 in the other) are given in Requirements and hosting; do not declare a lower minimum than your code really works with.
Stability tags and the channel element
A <tags> block holds dev, alpha, beta, rc or stable; only the last tag counts and no tag means stable. The value becomes an integer through stabilityTagToInteger(), and an entry is dropped when its stability is below the site’s Minimum Stability option of the Extensions component. Unknown strings fall back to stable, so a typo such as release-candidate silently publishes a release candidate to everyone. Publish pre-releases in a separate file or entry with an explicit rc tag and use a staging site with a lower minimum stability to test them.
An optional <channel> element restricts an entry to a release channel; when the site has no channel configured or the entry has none, it matches. The channel is used by core’s update site (the Default and Next channels mentioned in Upgrading Joomla 5 to 6); third-party extensions rarely need it.
Download elements
downloadurl has type (full or upgrade) and format (zip, tar, …). One or more downloadsource elements with the same attributes are mirrors: when the first download fails, the installer tries the next source and appends the download key to each. Keep every URL on one line with no surrounding whitespace, or the installer reports a malformed URL. The archive name should match the URL, and the URL should be HTTPS only.
infourl, changelogurl and the security element
infourl links to a release page and accepts a title. changelogurl links to the changelog XML described next. The optional <security> element is an integer severity (0 None to 4 Critical) that Joomla 6.2 (and 5.4) shows as a badge in the update view and on the dashboard; the displayed value for an extension is the highest among all newer releases. It is information supplied by the update server and not a verdict by the Joomla project. Set it only for releases that really fix a vulnerability, and read Security in extensions for what to publish and when. The mechanism is described from the administrator’s side in the page on extensions and update sites.
Changelogs
The changelog button in the Extensions Manager and the Update view is fed by a separate XML file named in changelogurl, in the manifest (for the Manage screen) and in the update entry (for the Update screen):
<?xml version="1.0" encoding="UTF-8"?>
<changelogs>
<changelog>
<element>pkg_tallpine</element>
<type>package</type>
<version>1.4.1</version>
<security>
<item>Escaped the trail title in the conditions banner.</item>
</security>
<fix>
<item>The elevation filter ignored the upper bound.</item>
</fix>
<addition>
<item>Dog-friendly tag on trail cards.</item>
</addition>
<note>
<item><![CDATA[Requires <strong>PHP 8.3</strong> or later.]]></item>
</note>
</changelog>
</changelogs>
Source: Joomla Manual: Changelogs
Each changelog needs element, type and version; the change types are security, fix, language, addition, change, remove and note, each with item children. HTML must be wrapped in CDATA. If the file has no entry for the version, the button is not shown, so add the entry to the file in the same release step that publishes the zip.
Checksums, signing and trust
The three optional elements sha256, sha384 and sha512 hold the hex digest of the package. InstallerHelper::isChecksumValid() computes hash_file() for every algorithm present, lowercases and trims the published value and compares: any mismatch aborts the update with an error, all matching gives success, and none present gives only a warning that the package could not be verified. Publish at least sha512 (several can be present and all must match).
# Build the package, then compute the digest published in the update XML
zip -r build/pkg_tallpine-1.4.1.zip pkg_tallpine.xml packages/ script.php
sha512sum build/pkg_tallpine-1.4.1.zip | cut -d' ' -f1
Source: Joomla Manual: Update Servers
It is important to understand what this does and does not protect. The checksum lives in the same XML as the download link, so it detects a corrupted or swapped zip only if the XML is intact: an attacker who controls the update server or the TLS connection changes both. There is no developer signature that Joomla verifies for third-party extensions; the TUF mechanism is not available to extension update sites, and the installer does not check a package signature. Practical consequences for a developer:
-
serve the XML and the zips over HTTPS with a valid certificate, and keep the update server host hardened and separate from the club website that has many editors;
-
store the digest outside the web root build step (generate the XML in CI rather than editing it by hand), and keep repository and release credentials in CI secrets;
-
sign git tags and release artefacts yourself (for example with GPG or Sigstore) and publish the public key and digest on the project page, so users can verify manually;
-
never reuse a version number for a different zip, since sites cache the update list for the configured cache time;
-
for an administrator, the same facts mean that a missing checksum warning should be taken seriously and that update sites of abandoned extensions should be disabled.
Download keys for paid extensions
A commercial extension adds a dlid element to its manifest, so that the Update Sites screen shows a Download Key field for it:
<!-- com_tallpine.xml (excerpt): premium trail packs -->
<dlid prefix="dlid=" suffix="" />
<updateservers>
<server type="extension" name="Tallpine Premium">https://updates.tallpine.example/premium.xml</server>
</updateservers>
Source: Joomla Manual: Update Servers
InstallerHelper::getDownloadKey() reads the prefix and suffix attributes from the installed manifest. When the administrator saves the key, UpdatesiteModel stores prefix + key + suffix in the extra_query column and deletes the cached #__updates rows so the next check applies it. At download time UpdateModel appends extra_query to the downloadurl and to each downloadsource, with ? or & depending on whether the URL already contains a query string. The server therefore sees .../premium.zip?dlid=<key> and can answer with the zip, with 403, or with a redirect.
A good server design for Tallpine’s premium packs:
<?php
// premium.php on the vendor's server: validate the key, then stream the zip
$key = $_GET['dlid'] ?? '';
if (!preg_match('/^[A-Za-z0-9]{32}$/', $key) || !isValidKey($key)) {
http_response_code(403);
exit;
}
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="com_tallpine-premium.zip"');
readfile(__DIR__ . '/private/com_tallpine-premium.zip');
Validate the format before touching the database, log failures without logging the key, throttle repeated attempts and compare keys against hashed values. The Manual’s Update Servers page does not describe download keys; the source tree is the reference. The key is a credential that lives in the customer’s database and its backups, and it travels in the URL, so it can appear in web server logs: use HTTPS and avoid returning it in redirects. The Update Sites screen can also filter by key state (supported, missing, valid), which helps administrators find sites that never received their key.
Publishing from a pipeline
Hand-edited XML goes stale. The Tallpine club generates the file from the release tag and publishes it to a static host:
# .github/workflows/release.yml (excerpt)
name: release
on:
push:
tags: ['v*']
jobs:
package:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Build package
run: |
mkdir -p build
zip -r "build/pkg_tallpine-${GITHUB_REF_NAME#v}.zip" pkg_tallpine.xml packages script.php
- name: Generate update XML
env:
VERSION: ${{ github.ref_name }}
run: |
V="${VERSION#v}"
SUM="$(sha512sum "build/pkg_tallpine-$V.zip" | cut -d' ' -f1)"
sed -e "s/@VERSION@/$V/g" -e "s/@SHA512@/$SUM/g" update.xml.dist > build/pkg_tallpine.xml
- uses: actions/upload-artifact@v4
with:
name: update-site
path: build/
Source: Joomla Manual: Update Servers
A later job copies build/ to the update host with a deploy key held in CI secrets. Keep the previous update entries when you regenerate the file (append rather than overwrite) so that sites still on older Joomla series keep seeing their releases.
Checking the file before you publish
curl -sS https://updates.tallpine.example/pkg_tallpine.xml | xmllint --noout - && echo "well-formed"
curl -sSI https://updates.tallpine.example/dl/pkg_tallpine-1.4.1.zip | head -n 1
curl -sS https://updates.tallpine.example/dl/pkg_tallpine-1.4.1.zip | sha512sum
Source: Joomla Manual: Update Servers
Then install the previous release on a Joomla 6.2 test site (see Local development with Docker), run Check for Updates with the cache cleared and confirm that the update appears, that the changelog button works and that the update installs. Repeat on the oldest Joomla series you claim to support.
Common errors and pitfalls
-
No update appears:
element,type,folderorclientdiffer from the installed row;targetplatformdoes not match the running version; the site’s PHP or database is below the minimum (check for the warning); the stability tag is below the site’s minimum; or the cached list has not expired. -
"Malformed" or empty download URL: whitespace or a line break inside
downloadurl, or an XML entity problem (&must be written&). -
Checksum errors: the digest was calculated before the zip was rebuilt, or upper and lower case differ in the file name that was hashed.
-
Premium update fails with 403: the key was not saved for that update site, or the prefix and suffix in the manifest do not match what the server expects.
-
Duplicate prompts: the package and its child extensions all declare update servers.
-
Update site missing after a restore: use Rebuild on the Update Sites screen; stored download keys survive it.
-
A new Joomla minor hides updates: the version pattern was written for a closed range of series.
The Joomla Extensions Directory
The JED at https://extensions.joomla.org/ is the official catalogue of extensions; the administrator reaches it from System → Install → Extensions with Install from Web, and the same listings are linked from the User Guide. Listing is voluntary, and the directory is a catalogue and not a store: the download link points to the developer’s own site and the extension is installed through the Joomla installer.
Listing rules
The JED Terms of Service, which every developer accepts, set the rules (section numbers as in the published text):
-
Licence (sections 3.1, 4.1 and 4.2): listed extensions must be GPL-licensed and comply with the latest Joomla licence, with no additional restrictions. Images, CSS and JavaScript need not be GPL, but their use may not be limited. The developer’s site must state the GPL licence and must not imply that being listed is an endorsement.
-
Installer: extensions that do not use the Joomla installer are not listed.
-
Checklist (section 4.1.5): listings must comply with the JED submission checklists, and a non-compliant listing may be unpublished immediately.
-
Paid extensions (sections 4.1 and 4.4): developers may charge for GPL extensions; any extension that requires payment or trade to download, or that bridges to a paid service needed to function, must be listed as paid. Changing between free and paid removes earlier votes and reviews.
-
Maintenance and removal (sections 4.3, 4.4 and 7): out-of-date content or rule breaches can lead to unpublishing; an unpublished listing has 14 days to be corrected before permanent removal; violations lead to suspension of one month, three months and then indefinitely.
The Terms of Service point to two update-related help pages (the update XML file and the Joomla update system requirement) without restating them; the practical rule for a developer is that a listed extension should have a working update server that follows the XML above.
JED Checker
The JED Checker is a Joomla extension that inspects an extension package against the JED standards before submission, covering the files that reviewers look at: the manifest, the presence of an update server, licence headers, direct access guards, use of deprecated or insecure code and similar items. It is installed like any other extension and is useful for packages that are not listed, as a pre-release gate. Run it on the final zip and fix findings before submitting; combine it with the static analysis and security review described in Coding standards and static analysis. Because a checker only looks at patterns, passing it does not mean the code is secure.
Joomla 6 compatibility and the badges
Visitors filter listings by supported Joomla version. At the check date, the Browse Extensions menu offered the filters Compatible with J4, Compatible with J5 and Compatible with J5 (with b/c plugin). The third filter is the one that matters for the move to Joomla 6: an extension that runs on 5.x only because of the backward-compatibility plugin is separated from one that does not need it. Joomla 6.0 removed or relocated APIs (the legacy J* class aliases, \Joomla\CMS\Input, \Joomla\CMS\Filesystem, the JPATH_PLATFORM constant and others) that remain available only through the Behaviour - Backward Compatibility 6 (BC6) plugin, which is a bridge and not a permanent feature (parts of it are slated for removal in Joomla 7). The sensible release policy is:
-
test the package on 5.4 and 6.2 with BC6 disabled, and declare support only for series you actually tested;
-
set
targetplatformandphp_minimumaccordingly in the update XML, so that sites on an unsupported combination see a warning instead of a broken update; -
record in the changelog when a release requires BC6;
-
update the JED listing’s version compatibility when a release changes it, since the badge and filters come from what the developer states.
See Deprecations and preparing for Joomla 7 and Versions, release cycle and support for which series are supported, and Upgrading Joomla 5 to 6 for the site-side steps. Older books such as Developing Extensions for Joomla! 5 (2023) describe JED Checker and compatibility labels for Joomla 3 to 5 only.
Security notes
-
A compromised update server can deliver code to every site that trusts it, so protect it like production: MFA on the hosting and registrar accounts, restricted deploy keys, monitoring of the XML for unexpected changes.
-
Never publish a download key, a Joomla
secretor deploy credentials in the repository, the XML or the zip; CI reads them from secrets. -
Keep release notes honest: describe security fixes specifically, and use the
<security>severity only when it applies. -
Remove old releases that contain known vulnerabilities from the public download area, or at least from the update XML.
Version notes
-
The
<security>element for updates arrived in Joomla 6.2 and was backported to 5.4. -
changelogurlis displayed since Joomla 4.0;php_minimumandsupported_databasesare evaluated by the 6.2 updater as described above. -
The
targetplatformmatch uses the version of the running Joomla, and Joomla 5.1.1 added the ability for the updater to evaluate the version of a target release (used when checking extensions before a major upgrade). -
Core updates are TUF-verified since 5.1; extension update sites are not.