Upgrading to Release 2026.2
10 minute read
This page covers what changed in Release 2026.2.10, how to prepare your environment, known compatibility risks, and how to resolve common post-upgrade errors.
What is this upgrade?
CloudBolt upgraded the underlying framework and runtime of the Cloud Management Platform (CMP) to Django 5.2 and Python 3.12, and updated Django REST Framework (DRF) from 3.11.2 to 3.15.2. These upgrades move CMP to the latest long-term support (LTS) versions of these technologies.
Why is this upgrade happening?
Django 3.2 and Python 3.9 have reached their official end of life and no longer receive security patches or bug fixes. Continuing to run end-of-life software creates security and compliance risk. Upgrading to Django 5.2 and Python 3.12 ensures CMP remains secure, supported, and aligned with modern software standards.
What are Django 5.2 and Python 3.12?
As of Release 2026.2.10, Django 5.2 is the latest LTS release of the Django framework, with security and bug-fix support through at least April 2028. Python 3.12 is a modern, actively maintained runtime with continued upstream support until October 2028. Together, they provide a stable, well-supported foundation for CMP for the years ahead.
Impact on your environment
Will I notice any changes after the upgrade?
Most changes are under the hood: improved security, better performance, and a more modern foundation. Day-to-day workflows, dashboards, and standard integrations should continue to function as expected.
How long will the upgrade take?
Upgrade duration depends on your database size. No additional downtime is expected beyond what is normal for any CMP upgrade.
Do I need to take any steps to prepare my environment?
Yes. Follow these steps before upgrading:
- Back up your environment. Back up your filesystem and database before upgrading or running any compatibility scripts. This provides a clean recovery point if any issues occur.
- Run the compatibility script. Execute the platform-provided compatibility script against your extension code on a staging branch. This script automatically updates deprecated Django import patterns.
- Upgrade your staging environment. Upgrade CMP as you would for any release.
- Validate extension behavior. After running the script, verify that your extensions behave as expected in staging.
- Review third-party packages. Check any third-party packages bundled within your extensions to ensure they are compatible with Django 5.2, Python 3.12, and DRF 3.15.2.
- Spot-check your CMP instance. Log in, run any Continuous Infrastructure Tests you have, and try common tasks such as refreshing server info or running a common workflow.
- Proceed to production. Once extensions and dependencies have been validated in staging, proceed with the production upgrade.
Extension compatibility script
To help automatically find and fix the most common deprecated code patterns, CloudBolt provides a script that scans extension code and rewrites deprecated imports and settings in place. The script covers all Django deprecations listed in the Django deprecations section of this page.
Run the script against a branch or staging copy of your extension code, then run your test suite to validate the results before proceeding to production.
./apply_django_upgrades.sh
The script rewrites files in place. Always run it against a branch or staging copy, not directly against your production extension code.
Risks and compatibility
What are the biggest risks of this version jump?
Because this upgrade spans multiple major versions, it carries more complexity than a routine release update. Deprecations and breaking changes from every release between Django 3.2 and 5.2 all apply at once. The primary areas of concern are:
- Custom integrations or API connections that rely on deprecated Django behaviors
- Third-party plugins or extensions that have not been verified for Django 5.2 and Python 3.12 compatibility
- Customizations built directly on top of Django or the admin interface
What about custom integrations and API connections?
Integrations that use standard, well-documented APIs are generally low risk. Custom-built integrations that interact directly with the CMP backend should be tested in a non-production environment before upgrading. Contact your TAM, CSM, or the support team for guidance specific to your environment.
Are there database compatibility concerns?
No. If your CMP appliance has been upgraded at any point in the last three years, you should be running PostgreSQL 14 and will not be impacted. A database version upgrade is planned for later in the year.
What should I do if something breaks after upgrading?
Contact support or your TAM immediately if you experience issues. CloudBolt will prioritize post-upgrade issues reported within the first three months of the GA release.
Is this upgrade compliant with my security and audit requirements?
Yes. Staying current on framework versions directly reduces compliance exposure. Running software on an end-of-life framework like Django 3.2 is a known audit flag under SOC 2, ISO, and NIST. Upgrading to Django 5.2 LTS directly addresses that and improves alignment with software lifecycle best practices.
What if I have concerns about my specific environment?
Reach out to your Customer Success Manager, Technical Account Manager, or the Support team. They can walk through your specific configuration, discuss potential risks, and help you plan for a smooth upgrade.
Django deprecations: what was removed and what to fix
The following Django APIs were deprecated across the 3.x–5.x lifecycle and are no longer available in Django 5.2. The compatibility script handles all of these automatically.
URL configuration
The legacy url() function has been removed. Extensions must use path() or re_path() from django.urls.
| Removed | Replacement |
|---|---|
from django.conf.urls import url |
from django.urls import re_path |
Translation utilities
The ugettext family of functions (a Python 2-era holdover) has been fully removed.
| Removed | Replacement |
|---|---|
ugettext(), ugettext_lazy(), ugettext_noop() |
gettext(), gettext_lazy(), gettext_noop() |
ungettext(), ungettext_lazy() |
ngettext(), ngettext_lazy() |
Encoding utilities
| Removed | Replacement |
|---|---|
django.utils.encoding.force_text() |
force_str() |
django.utils.encoding.smart_text() |
smart_str() |
Text utilities
| Removed | Replacement |
|---|---|
django.utils.text.unescape_entities() |
Python’s built-in html.unescape() |
HTTP utilities
| Removed | Replacement |
|---|---|
django.utils.http.is_safe_url() |
django.utils.http.url_has_allowed_host_and_scheme() |
Django REST Framework 3.11.2 → 3.15.2: what changed
This is a significant version jump for DRF. The changes below span releases 3.12 through 3.15 and represent the cumulative customer-facing impact.
serializer.is_valid(True) no longer accepted (3.14)
Calling is_valid() with a positional argument is no longer valid. The raise_exception argument must now be passed as a keyword argument.
| Before | After |
|---|---|
serializer.is_valid(True) |
serializer.is_valid(raise_exception=True) |
Permission OR semantics corrected (3.14)
The behavior when combining two permission classes with | (OR) has been corrected. Previously, has_permission() was ignored on both classes; now, a request must pass has_permission() on at least one class. Test extensions with composed permissions to confirm access control behaves as intended.
NullBooleanField removed (3.14)
| Removed | Replacement |
|---|---|
serializers.NullBooleanField() |
serializers.BooleanField(allow_null=True) |
SearchFilter behavior aligned to Django admin (3.15)
SearchFilter now aligns with Django’s admin search behavior, including support for quoted phrases with spaces (each treated as a single search term) and raising a validation error if a null character is included. Extensions with custom search implementations or those calling get_search_terms should be reviewed.
pytz dependency removed (3.15)
DRF no longer depends on pytz. Django now provides ZoneInfo instances for timezone handling (as long as USE_DEPRECATED_PYTZ is not enabled). Extensions that rely on pytz timezone objects in DRF contexts should be updated to use zoneinfo.
UniqueConstraint validation tightened (3.15)
Serializer validation for UniqueConstraint with nullable fields has been corrected. Extensions that relied on the previous (incorrect) behavior — where certain nullable unique field combinations passed validation they should not have — will now receive validation errors.
Serializer field default propagation (3.15)
Serializer fields now correctly inherit default values from the underlying model field. Extensions in which a serializer field previously had no default but the model field did may now behave differently during partial updates or when handling optional fields.
Python 3.9 → 3.12: impactful deprecations for customer code
collections aliases removed (Python 3.10)
Direct imports like collections.Mapping or collections.Callable have been removed. Extensions must import from collections.abc instead (for example, collections.abc.Mapping). Python 3.9 was the final version to support these Python 2 compatibility shims.
distutils removed (Python 3.12)
The distutils package has been removed from the standard library. Extensions or build scripts using distutils must migrate to setuptools or packaging.
asynchat, asyncore, smtpd modules removed (Python 3.12)
These legacy async modules no longer exist. Extensions using them must be rewritten using asyncio.
Stricter exception handling (Python 3.11+)
More explicit error reporting can surface previously hidden errors in code that catches broad exceptions. Extensions using bare except: or overly broad except Exception: blocks should be reviewed.
Configuring JWT token lifetime
Why did the JWT token configuration change?
CloudBolt has migrated from djangorestframework-jwt to djangorestframework-simplejwt. As part of this change, the setting that controls token expiration has been updated. The old setting is silently ignored, which is why tokens may appear to expire sooner than expected.
What are the default token lifetimes?
| Token type | Default lifetime |
|---|---|
| Access token | 5 minutes |
| Refresh token | 1 day |
How do I change the access token lifetime?
Add or update the SIMPLE_JWT setting in /var/opt/cloudbolt/proserv/customer_settings.py:
from datetime import timedelta
# Example: set access token lifetime to 24 hours
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(hours=24),
}
You can use any timedelta unit: seconds, minutes, hours, days.
Define the whole dictionary rather than assigning to one of its keys: customer_settings.py replaces the built-in SIMPLE_JWT, and any key you leave out falls back to its default.
How do I change the refresh token lifetime?
from datetime import timedelta
# Example: set refresh token lifetime to 7 days
SIMPLE_JWT = {
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
}
Can I set both at the same time?
Yes:
from datetime import timedelta
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(hours=24),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
}
Why doesn’t my old JWT_AUTH setting work anymore?
The previous library (djangorestframework-jwt) used a JWT_AUTH dictionary with keys like JWT_EXPIRATION_DELTA. The new library (djangorestframework-simplejwt) does not read these keys. Any setting under JWT_AUTH that controls token lifetime is now ignored.
| Old (no longer works) | New (required) |
|---|---|
JWT_AUTH["JWT_EXPIRATION_DELTA"] = datetime.timedelta(hours=24) |
SIMPLE_JWT = {"ACCESS_TOKEN_LIFETIME": timedelta(hours=24)} |
Where should I make this change?
Add the setting to /var/opt/cloudbolt/proserv/customer_settings.py. Do not modify /opt/cloudbolt/settings.py directly, as that file is managed by CloudBolt and may be overwritten on upgrade.
Do I need to restart the server after changing this?
Yes. After editing customer_settings.py, restart the CMP application server for the change to take effect. Tokens issued before the restart will retain their original expiration.
XUI errors in the upgrade log
Why am I seeing XUI errors in the upgrade log?
These errors occur because the upgrade brings a newer version of Django that removed some older APIs that XUI extensions may still reference. This is expected behavior and does not indicate a problem with the upgrade itself. These errors are safe to ignore and will be fully resolved by running the fix script.
Example from an upgrade log:
2026-04-24 15:11:26,081 [ERROR] extensions.views: Failed to import UI extension in xui.bolty_cards.views
Traceback (most recent call last):
File "./src/extensions/views.py", line 726, in import_module
File "/var/opt/cloudbolt/proserv/xui/bolty_cards/views.py", line 14, in <module>
from xui.bolty_cards.forms import GuessForm
File "/var/opt/cloudbolt/proserv/xui/bolty_cards/forms.py", line 3, in <module>
from django.utils.translation import ugettext_lazy as _lazy
ImportError: cannot import name 'ugettext_lazy' from 'django.utils.translation'
In this example, ugettext_lazy is a deprecated Django API removed in Django 4.0, causing the entire extension to fail to load.
Why do I see “Failed to import UI extension URLs” warnings?
These URL warnings are a downstream side effect of the import errors above. When an extension fails to load, its URL configuration also fails. They are not a separate issue and do not require individual attention. Running the fix script resolves them.
How do I fix these errors?
Run the provided compatibility script to automatically update all deprecated Django API calls across your XUI extensions:
./apply_django_upgrades.sh
This script handles all known removals and restores full XUI functionality after the upgrade. See the Django deprecations section for the full list of affected APIs.
Python package compatibility
What happens to my custom Python packages when I upgrade?
When you upgrade, the upgrader installs Python 3.12. While all packages installed under Python 3.9 remain on disk, you will need to reinstall them for Python 3.12.
Will reinstalling packages fix compatibility issues?
In most cases, yes. Reinstalling ensures you get package versions that support Python 3.12. However, if a package does not yet support Python 3.12, you may need to check for updates or find an alternative.
Content library
Is the content in the Library already updated for compatibility?
Yes. CMP content has always included minimum and maximum version metadata. If content appears in your appliance, it has been deemed compatible with your current version.
What happens to resources already downloaded from the Library?
Some already-imported resources will be fixed automatically by the ./apply_django_upgrades.sh script. However, blueprints impacted by the Azure SDK update will not be auto-fixed — you will need to re-import them from the Library. Updated items will be highlighted under What’s New.
Blueprints updated due to the Azure SDK update:
- Azure Storage Blob
- Azure Storage File
- Azure Databricks
XUIs updated due to the Azure SDK update:
- Azure ARM Template