Upgrading to Release 2026.3
12 minute read
Release 2026.3.51 moves the Cloud Management Platform (CMP) interface from Bootstrap 3 to Bootstrap 5. Bootstrap 3 is no longer supported, and no compatibility layer is provided. This page covers what changed and how to migrate custom templates, UI extensions, and JavaScript.
Who this affects
This page applies to you if you maintain any of the following:
- Custom Django templates in
/var/opt/cloudbolt/proserv/that extendbase.htmlor any CMP template - UI extensions (XUIs) you wrote yourself, or modified after installing
- Custom JavaScript that calls
c2.*functions
CloudBolt UI extensions
CloudBolt UI extensions have been updated for Bootstrap 5 and republished in the Content Library. If an updated version is not listed yet, refresh the Library listing.
- If you have not modified the extension, re-import it from the Library. You do not need the rest of this page.
- If you modified the extension after installing it, re-importing replaces your copy, and you cannot recover your changes. Either back up your copy, re-import, and reapply your changes using this page, or migrate your copy in place using this page.
What changed
Bootstrap 5 renamed or removed almost all of the Bootstrap 3 class names and data attributes. This means:
- Markup that uses them renders as unstyled elements.
- Interactive widgets built with legacy
data-*attributes do not open, close, or switch.
Check whether your templates need to be updated
Run these read-only checks on the appliance. Test any changes on a staging appliance before applying them in production.
1. Find template blocks that no longer exist
grep -rnE '\{% *block +(breadcrumbs|extrastyle|fluidclass|outercontent|scripts) *%\}' /var/opt/cloudbolt/proserv/
Each match is a block override that is silently discarded and must be renamed. See Template blocks that no longer exist.
2. Find Bootstrap 3 markup
grep -rniE 'panel-default|panel-heading|panel-body|panel-footer|control-label|btn-default|form-horizontal|form-group|data-toggle=|data-target=|data-dismiss=|data-placement=|pull-left|pull-right|col-xs-|img-responsive|center-block|help-block|input-group-addon|table-condensed|glyphicon|hidden-(xs|sm|md|lg)|visible-(xs|sm|md|lg)|sr-only|\bwell\b|\b(mr|ml|pr|pl)-[0-5]\b' /var/opt/cloudbolt/proserv/
Review each match against the class mapping and data attributes tables. Most of these no longer function and must be migrated. Five of them still work, as described in the class mapping.
The check also catches the Bootstrap 4 spacing utilities mr-*, ml-*, pr-*, and pl-*, which Bootstrap 5 renamed.
3. Dashboard UI extensions only: find renamed card classes
Run this check only if you maintain a UI extension that renders as a dashboard widget.
grep -rnE 'card-heading|card-default' /var/opt/cloudbolt/proserv/
Update any matches in your stylesheet or JavaScript. See Dashboard UI extensions.
Template blocks that no longer exist
While your {% extends %} line continues to work, the old page shell defined five blocks that the new one does not: breadcrumbs, extrastyle, fluidclass, outercontent, and scripts.
In Django, a child template can override only blocks its parent chain defines. Content is discarded without warning if you override a name the parent does not have. The page renders without the block’s contents.
Rename each override to its Bootstrap 5 equivalent:
| No longer exists | Use instead | Notes |
|---|---|---|
extrastyle |
head |
Put <link rel="stylesheet"> and <style> tags here. Do not use inlinecss, which is already inside a <style> element. |
scripts |
body_scripts |
Renders just before </body>, in the same place as the old block. Unlike the old block, it already contains CMP code that shows server messages and runs the c2script and onReady blocks. Start your override with {{ block.super }} to retain that code. |
outercontent |
content |
CMP adds the surrounding page layout. Include only your own content. |
breadcrumbs |
breadcrumb-item |
This block is already inside a <li class="breadcrumb-item active"> element. Include only the current page name, without <ol> or <li> tags. |
fluidclass |
No equivalent | CMP sets the page width. Remove the override. |
Only top-level overrides of these names are discarded. A block with one of these names still renders if it is inside a block that exists, such as {% block content %}.
Class mapping
Use this table to replace Bootstrap 3 classes.
Bootstrap 3 classes btn-default, form-horizontal, help-block, well, and the glyphicon icon font continue to function. The CMP stylesheet defines them in Bootstrap 5 terms because the CMP UI uses them. They are not scheduled for removal, so updating them is optional but recommended.
| Bootstrap 3 | Bootstrap 5 | Notes |
|---|---|---|
panel panel-default |
card |
Also replace panel-heading with card-header, panel-body with card-body, and panel-footer with card-footer.Bootstrap 3’s panel carried a bottom margin, and Bootstrap 5’s card does not. Add mb-3 wherever panels were stacked, or they sit flush against each other.For tab and dashboard extensions, read Tabbed UI extensions or Dashboard UI extensions first. |
panel-title |
Remove | Style the heading element directly. |
control-label |
form-label |
|
form-group |
mb-3 |
Bootstrap 5 removed this class. |
form-horizontal |
row and col-* on each field |
Bootstrap 5 removed horizontal forms. form-horizontal still works. |
help-block |
form-text |
help-block still works. |
btn-default |
btn-light |
Matches the rest of the CMP interface and uses the portal’s button theme. btn-default still works. |
cb-btn / cb-btn-primary |
btn btn-sm / btn-primary |
Legacy CMP classes. cb-btn-primary does not use the portal’s primary color. cb-btn is a small button, so add btn-sm to keep the size. |
btn-xs |
btn-sm |
Bootstrap 5 has no extra-small size. |
input-group-addon |
input-group-text |
Place it directly inside .input-group, and remove any .input-group-btn wrapper. |
pull-left / pull-right |
float-start / float-end |
|
text-left / text-right |
text-start / text-end |
|
mr-* / ml-* (Bootstrap 4) |
me-* / ms-* |
Bootstrap 5 renamed left and right to start and end, and the old names do nothing. Replace pr-* and pl-* with pe-* and ps-*. mt-*, mb-*, pt-*, and pb-* are unchanged. |
col-xs-N |
col-N |
The class without a size prefix is now the smallest size. |
hidden-xs, visible-sm, and similar |
d-none, d-sm-block, and similar |
There is no one-to-one replacement. Check each case. |
hidden |
d-none |
|
sr-only |
visually-hidden |
|
img-responsive |
img-fluid |
|
center-block |
mx-auto d-block |
|
table-condensed |
table-sm |
|
label label-default |
badge text-bg-secondary |
Replace other label-* classes with the matching text-bg-* class. |
badge (rounded) |
badge rounded-pill |
Bootstrap 5 badges are square by default. |
well |
card card-body |
Or bg-body-secondary p-3 rounded. well still works. |
caret |
Remove | dropdown-toggle adds its own arrow. |
close (modal close button) |
btn-close |
Remove the × content. |
glyphicon glyphicon-* |
Bootstrap Icons or Font Awesome | Glyphicons still work. For new work, use the same icons as the surrounding CMP pages. |
icon-* (CloudBolt legacy icons) |
bi bi-* (Bootstrap Icons) |
A temporary compatibility layer maps some names to Bootstrap Icons, such as icon-edit, icon-delete, icon-add, and icon-verify-connection. It does not cover every name. For example, icon-sync displays nothing. Replace icon-* classes when you edit the markup. |
Data attributes
Bootstrap 5 adds bs- to every Bootstrap data attribute and ignores the old names, so a modal with data-toggle="modal" does not open.
| Bootstrap 3 | Bootstrap 5 |
|---|---|
data-toggle |
data-bs-toggle |
data-target |
data-bs-target |
data-dismiss |
data-bs-dismiss |
data-placement |
data-bs-placement |
data-parent |
data-bs-parent |
data-ride, data-slide, data-slide-to, data-spy |
The matching data-bs-* attribute |
Rename only Bootstrap attributes. Do not rename your own data-* attributes, or CMP attributes such as data-tooltip-source.
Components that need more than a rename
Tabs and pills
In Bootstrap 5, the active class moves from the <li> to the link; the link needs the nav-link class, and each pane needs role="tabpanel".
Bootstrap 3:
<ul class="nav nav-tabs">
<li class="active"><a href="#one" data-toggle="tab">One</a></li>
<li><a href="#two" data-toggle="tab">Two</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="one">First</div>
<div class="tab-pane" id="two">Second</div>
</div>
Bootstrap 5:
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" type="button" role="tab"
data-bs-toggle="tab" data-bs-target="#one"
aria-controls="one" aria-selected="true">One</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" type="button" role="tab"
data-bs-toggle="tab" data-bs-target="#two"
aria-controls="two" aria-selected="false">Two</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="one" role="tabpanel">First</div>
<div class="tab-pane fade" id="two" role="tabpanel">Second</div>
</div>
Modals
The modal markup is unchanged. Update the trigger attributes and the close button, which no longer needs ×:
<!-- Bootstrap 3 -->
<button data-toggle="modal" data-target="#mymodal">Open</button>
<button class="close" data-dismiss="modal">×</button>
<!-- Bootstrap 5 -->
<button data-bs-toggle="modal" data-bs-target="#mymodal">Open</button>
<button class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
Accordions
Rewrite Bootstrap 3 accordions (panel-group with collapse) using the Bootstrap 5 accordion component, with accordion-item, accordion-header, accordion-button, and accordion-collapse.
Forms
Replace form-group with mb-3, control-label with form-label, and help-block with form-text:
<!-- Bootstrap 3 -->
<div class="form-group">
<label class="control-label">Name</label>
<input type="text" class="form-control">
<span class="help-block">Helper text</span>
</div>
<!-- Bootstrap 5 -->
<div class="mb-3">
<label class="form-label">Name</label>
<input type="text" class="form-control">
<div class="form-text">Helper text</div>
</div>
Dark mode
The CMP interface has a dark theme, and Bootstrap 3 markup often assumes a light background. When you migrate:
- Do not use
bg-light,text-bg-light, orbg-white. They stay light in dark mode, which makes text unreadable. Usebg-body-secondaryinstead, andvar(--fg-1)if you need to set the text color. - Do not hard-code hex colors for backgrounds, borders, or text. Use Bootstrap utility classes or CMP CSS custom properties.
Check each migrated page in both the light and dark themes.
JavaScript changes
If your code calls c2.* functions, check it against the following lists.
Removed modules
These modules no longer exist. Calling them causes a TypeError:
c2.animationc2.cuic2.datepicker(see Date and time picker)c2.datetimepicker(see Date and time picker)c2.iframesc2.progressBarsc2.quickSetupc2.scrollingc2.textc2.topologyc2.visibility
A TypeError also stops any later scripts on the page from running. In the browser console, fix the first error before checking the others.
Renamed functions
These still work under their old names:
-
c2.tooltipis now an alias forc2.tooltips.closeAllwas removed.initworks differently:- It only finds elements with
data-bs-toggle="tooltip". Elements that still usedata-toggleget no tooltip. - It only applies to elements already on the page. Call it again after you add content.
For new code, use
c2.tooltips.init(selector). - It only finds elements with
-
The server statistics chart functions moved to
c2.chartsServerStats, and are still available onc2.charts.c2.charts.pieandc2.charts.barare unchanged.
Removed functions
These functions were removed with no replacement:
| Module | Removed functions |
|---|---|
c2.accessibility |
alert, alertAll |
c2.blueprints |
initCatalog, initFilters |
c2.charts |
buildGroupBillingBarSeries, buildServerBillingSeries, serverPieChartDefaults |
c2.commonEventHandlers |
aceOnPanelOpen, aceOffPanelOpen, popovers |
c2.dataTables |
dialogDefaults, isDataTable |
c2.forms |
appendShowAllButton, hide_add_link_when_depleted, moveOffsetLabelsToLeftCol, splitElementsIntoColumns |
c2.orchestrationActions |
init |
The removed c2.charts functions are not available in c2.chartsServerStats either.
What to use instead
| Previously | Now |
|---|---|
c2.datepicker, c2.datetimepicker |
Nothing. Pickers now work from the markup. See Date and time picker. |
| Scripting Bootstrap components | The Bootstrap 5 classes directly: bootstrap.Modal, bootstrap.Tab, bootstrap.Tooltip, bootstrap.Dropdown, bootstrap.Toast, and bootstrap.Popover. |
| Switching tabs | c2.tabs.activate(hash), including the leading # (for example, "#build"). You do not need to call c2.tabs.init(), which runs on page load. |
Date and time picker
Date and time pickers work without code changes:
- Inputs with the
render_as_datepickerorrender_as_datetimepickerclass still get a picker. By default, values use theYYYY-MM-DDandYYYY-MM-DD HH:mm:ssformats, so server-side parsing is unchanged. - Pickers start on page load, and also on content added later, such as forms loaded with AJAX.
- Existing
$(input).datepicker(options)and$(input).datetimepicker(options)calls still work.
In new code:
- To start a picker, call
$(input).bs5DateTimePicker(options). - To start pickers in part of the page, call
$.bs5DateTimePicker.autoInit(element). - To set options on an input, use attributes such as
data-format,data-include-time,data-min-date, anddata-max-date.
Tabbed UI extensions
CMP displays a tab UI extension (built with TabGroup or {% draw_tabs %}) inside a frame that is already styled as a card, with a background, border, rounded corners, shadow, and padding.
Do not wrap the whole tab pane in a card, or the pane gets a double border. Mapping panel panel-default to card causes this. If a Bootstrap 3 tab used one panel around the whole pane, remove the panel and change the panel-heading text to a heading:
<!-- Bootstrap 3 -->
<div class="panel panel-default">
<div class="panel-heading">Jobs</div>
<div class="panel-body"><table class="table">...</table></div>
</div>
<!-- Bootstrap 5: the tab body is already the card -->
<h5 class="mb-3">Jobs</h5>
<table class="table">...</table>
Use cards inside a pane only to separate sections. If a pane had two or three stacked panels, change them to two or three cards, each with mb-3.
Dashboard UI extensions
CMP displays a dashboard UI extension inside a widget frame. To avoid showing a card inside a card, CMP unwraps the first element with the card class in your extension’s HTML, even if it is nested inside other elements:
- CMP removes the card’s
.card-headerand shows its.card-titleand.card-subtitlein the widget header. - CMP moves the elements inside
.card-bodyinto the widget body. Text that is not inside an element is lost. - CMP deletes the card, including anything else inside it, such as a
.card-footer.
What you need to do depends on whether your extension extends dashboard/card.html:
- If it does, you do not need to change your markup. The template now uses
cardandcard-headerinstead ofcard card-defaultandcard-heading. Update any of your own CSS or JavaScript that uses the old class names. Run the dashboard check to find them. - If it does not, CMP treats the first card in your HTML as the widget frame. To keep your card, give it a different class.
As in earlier releases, CMP also:
- Moves elements with the
modalclass to the end of the page, so modals open over the page instead of inside the widget. - Re-creates
<script>elements so that they run.
Scripts inside a .modal do not run, because CMP moves modals before it re-creates scripts. Keep widget scripts outside modals.
Verify your work
- Open the browser console on each page. Errors such as
bootstrap is not definedor an undefinedc2function point to code that needs to be migrated. Fix the first error before checking the others. - Open every modal, tab, dropdown, collapse, and tooltip. These fail without a console error if an attribute was missed.
- Confirm your stylesheets and scripts load in the browser’s network panel. A missing file can mean a template block was not renamed.
- Check each page in light and dark mode.
- Check each tab extension for a double border. A double border means a card is wrapping the pane.
- Check each dashboard widget extension on the dashboard, not only on its own page. Its title should appear in the widget header, with no empty card frame around the content.
- Check that every icon displays. A gap usually means an
icon-*name that the compatibility layer does not cover. - Run the checks again. The block check should return nothing. The class check should return only the five classes that still work, or matches you chose to keep.
Getting help
If a page still does not display correctly, contact CloudBolt Support by emailing support@cloudbolt.io, raising a support ticket, or contacting your Technical Account Manager. Include the template source and screenshots of the page in light and dark mode.
Related
- Front-end assets and tools: The libraries loaded by default, and how to ship your own CSS and JavaScript.
- UI elements that support extensions: The decorator and arguments for each extension type.
- Upgrading to Release 2026.2: Django 5.2 and Python 3.12 changes that also affect UI extensions.