diff --git a/extras/packaging/gnu-linux/Makefile b/extras/packaging/gnu-linux/Makefile
index 1eb2fe098bd8f459df401b35f15f0efdd43eecfa..1b410cede15564ff0dbf9acf163f85cd158f79fa 100644
--- a/extras/packaging/gnu-linux/Makefile
+++ b/extras/packaging/gnu-linux/Makefile
@@ -168,6 +168,7 @@ DISTRIBUTIONS := \
 	ubuntu_22.04 \
 	ubuntu_23.04 \
 	ubuntu_23.10 \
+	ubuntu_24.04 \
 	fedora_37 \
 	fedora_38 \
 	fedora_39 \
diff --git a/extras/packaging/gnu-linux/docker/Dockerfile_ubuntu_24.04 b/extras/packaging/gnu-linux/docker/Dockerfile_ubuntu_24.04
new file mode 100644
index 0000000000000000000000000000000000000000..deb61ec77d872b37d980862512d80d649d6b054c
--- /dev/null
+++ b/extras/packaging/gnu-linux/docker/Dockerfile_ubuntu_24.04
@@ -0,0 +1,25 @@
+FROM ubuntu:24.04
+
+ENV DEBIAN_FRONTEND=noninteractive
+
+RUN apt-get clean
+RUN apt-get update && \
+    apt-get install -y -o Acquire::Retries=10 \
+        devscripts \
+        equivs \
+        python-is-python3 \
+        wget
+
+ADD extras/packaging/gnu-linux/scripts/install-gcc-debian.sh /opt/install-gcc-debian.sh
+RUN /opt/install-gcc-debian.sh 13
+
+ADD extras/packaging/gnu-linux/scripts/prebuild-package-debian.sh /opt/prebuild-package-debian.sh
+
+COPY extras/packaging/gnu-linux/rules/debian-qt/control /tmp/builddeps/debian/control
+RUN /opt/prebuild-package-debian.sh qt-deps
+
+COPY extras/packaging/gnu-linux/rules/debian/control /tmp/builddeps/debian/control
+RUN /opt/prebuild-package-debian.sh jami-deps
+
+ADD extras/packaging/gnu-linux/scripts/build-package-debian.sh /opt/build-package-debian.sh
+CMD ["/opt/build-package-debian.sh"]
diff --git a/extras/packaging/gnu-linux/rules/debian-qt/control b/extras/packaging/gnu-linux/rules/debian-qt/control
index b2a302397689f73fabab19f22fcbde11c9b66cee..c97e2efe5d079db5d5c0145dbfce2df67da682bd 100644
--- a/extras/packaging/gnu-linux/rules/debian-qt/control
+++ b/extras/packaging/gnu-linux/rules/debian-qt/control
@@ -92,6 +92,7 @@ Build-Depends: debhelper (>= 9),
                libgl1-mesa-dri,
 # pkg-kde-tools (>= 0.15.17~),
                python3:any,
+               python3-bs4,
                python3-html5lib,
 # qtbase5-private-dev (>= 5.15.2+dfsg~),
                xauth <!nocheck>,
diff --git a/extras/packaging/gnu-linux/rules/debian-qt/patches/0001-replace_imp_by_importlib.patch b/extras/packaging/gnu-linux/rules/debian-qt/patches/0001-replace_imp_by_importlib.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ec89acfd15e662d5c9932c8b32fdcf9013f4680e
--- /dev/null
+++ b/extras/packaging/gnu-linux/rules/debian-qt/patches/0001-replace_imp_by_importlib.patch
@@ -0,0 +1,314 @@
+From deeacfdb5a6d1d300d4ba991df76aa12e5dbaa42 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?S=C3=A9bastien=20Blin?=
+ <sebastien.blin@savoirfairelinux.com>
+Date: Tue, 16 Apr 2024 09:54:32 -0400
+Subject: [PATCH] fix imp->importlib
+
+---
+ .../mojo/public/tools/mojom/mojom/fileutil.py    |  1 -
+ .../tools/mojom/mojom/fileutil_unittest.py       |  5 +----
+ .../mojom/mojom/generate/generator_unittest.py   |  7 ++-----
+ .../mojom/mojom/generate/translate_unittest.py   |  4 ----
+ .../tools/mojom/mojom/parse/ast_unittest.py      |  6 ------
+ .../mojom/parse/conditional_features_unittest.py |  8 ++------
+ .../mojo/public/tools/mojom/mojom/parse/lexer.py |  1 -
+ .../tools/mojom/mojom/parse/lexer_unittest.py    |  7 ++-----
+ .../tools/mojom/mojom/parse/parser_unittest.py   |  5 -----
+ .../3rdparty/chromium/third_party/six/src/six.py | 16 ++++++++++++++++
+ 10 files changed, 23 insertions(+), 37 deletions(-)
+
+diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py
+index 29daec367c..124f12c134 100644
+--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py
++++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py
+@@ -3,7 +3,6 @@
+ # found in the LICENSE file.
+ 
+ import errno
+-import imp
+ import os.path
+ import sys
+ 
+diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py
+index 48eaf4eca9..c93d22898d 100644
+--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py
++++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py
+@@ -2,19 +2,16 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+ 
+-import imp
+ import os.path
+ import shutil
+-import sys
+ import tempfile
+ import unittest
+ 
+ from mojom import fileutil
+ 
+-
+ class FileUtilTest(unittest.TestCase):
+   def testEnsureDirectoryExists(self):
+-    """Test that EnsureDirectoryExists fuctions correctly."""
++    """Test that EnsureDirectoryExists functions correctly."""
+ 
+     temp_dir = tempfile.mkdtemp()
+     try:
+diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
+index 76cda3981f..7143e07c4d 100644
+--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
++++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
+@@ -2,12 +2,11 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+ 
+-import imp
++import importlib.util
+ import os.path
+ import sys
+ import unittest
+ 
+-
+ def _GetDirAbove(dirname):
+   """Returns the directory "above" this file containing |dirname| (which must
+   also be "above" this file)."""
+@@ -20,12 +19,11 @@ def _GetDirAbove(dirname):
+ 
+ 
+ try:
+-  imp.find_module("mojom")
++  importlib.util.find_spec("mojom")
+ except ImportError:
+   sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+ from mojom.generate import generator
+ 
+-
+ class StringManipulationTest(unittest.TestCase):
+   """generator contains some string utilities, this tests only those."""
+ 
+@@ -69,6 +67,5 @@ class StringManipulationTest(unittest.TestCase):
+     self.assertEquals("SNAKE_D3D11_CASE",
+                       generator.ToUpperSnakeCase("snakeD3d11Case"))
+ 
+-
+ if __name__ == "__main__":
+   unittest.main()
+diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
+index 4259374513..558e71e119 100644
+--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
++++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
+@@ -2,16 +2,12 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+ 
+-import imp
+-import os.path
+-import sys
+ import unittest
+ 
+ from mojom.generate import module as mojom
+ from mojom.generate import translate
+ from mojom.parse import ast
+ 
+-
+ class TranslateTest(unittest.TestCase):
+   """Tests |parser.Parse()|."""
+ 
+diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
+index c36376712e..b289f7b11f 100644
+--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
++++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
+@@ -2,14 +2,10 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+ 
+-import imp
+-import os.path
+-import sys
+ import unittest
+ 
+ from mojom.parse import ast
+ 
+-
+ class _TestNode(ast.NodeBase):
+   """Node type for tests."""
+ 
+@@ -20,13 +16,11 @@ class _TestNode(ast.NodeBase):
+   def __eq__(self, other):
+     return super().__eq__(other) and self.value == other.value
+ 
+-
+ class _TestNodeList(ast.NodeListBase):
+   """Node list type for tests."""
+ 
+   _list_item_type = _TestNode
+ 
+-
+ class ASTTest(unittest.TestCase):
+   """Tests various AST classes."""
+ 
+diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
+index 5fc582025e..2fa5d2be6a 100644
+--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
++++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
+@@ -2,12 +2,11 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+ 
+-import imp
++import importlib.util
+ import os
+ import sys
+ import unittest
+ 
+-
+ def _GetDirAbove(dirname):
+   """Returns the directory "above" this file containing |dirname| (which must
+   also be "above" this file)."""
+@@ -18,9 +17,8 @@ def _GetDirAbove(dirname):
+     if tail == dirname:
+       return path
+ 
+-
+ try:
+-  imp.find_module('mojom')
++  importlib.util.find_spec("mojom")
+ except ImportError:
+   sys.path.append(os.path.join(_GetDirAbove('pylib'), 'pylib'))
+ import mojom.parse.ast as ast
+@@ -29,7 +27,6 @@ import mojom.parse.parser as parser
+ 
+ ENABLED_FEATURES = frozenset({'red', 'green', 'blue'})
+ 
+-
+ class ConditionalFeaturesTest(unittest.TestCase):
+   """Tests |mojom.parse.conditional_features|."""
+ 
+@@ -356,6 +353,5 @@ class ConditionalFeaturesTest(unittest.TestCase):
+                       conditional_features.RemoveDisabledDefinitions,
+                       definition, ENABLED_FEATURES)
+ 
+-
+ if __name__ == '__main__':
+   unittest.main()
+diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py
+index 73ca15df94..1083a1af7b 100644
+--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py
++++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py
+@@ -2,7 +2,6 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+ 
+-import imp
+ import os.path
+ import sys
+ 
+diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
+index ce376da66e..bc9f835431 100644
+--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
++++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
+@@ -2,12 +2,11 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+ 
+-import imp
++import importlib.util
+ import os.path
+ import sys
+ import unittest
+ 
+-
+ def _GetDirAbove(dirname):
+   """Returns the directory "above" this file containing |dirname| (which must
+   also be "above" this file)."""
+@@ -18,17 +17,15 @@ def _GetDirAbove(dirname):
+     if tail == dirname:
+       return path
+ 
+-
+ sys.path.insert(1, os.path.join(_GetDirAbove("mojo"), "third_party"))
+ from ply import lex
+ 
+ try:
+-  imp.find_module("mojom")
++  importlib.util.find_spec("mojom")
+ except ImportError:
+   sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+ import mojom.parse.lexer
+ 
+-
+ # This (monkey-patching LexToken to make comparison value-based) is evil, but
+ # we'll do it anyway. (I'm pretty sure ply's lexer never cares about comparing
+ # for object identity.)
+diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
+index 0513343ec7..0a26307b1a 100644
+--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
++++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
+@@ -2,16 +2,12 @@
+ # Use of this source code is governed by a BSD-style license that can be
+ # found in the LICENSE file.
+ 
+-import imp
+-import os.path
+-import sys
+ import unittest
+ 
+ from mojom.parse import ast
+ from mojom.parse import lexer
+ from mojom.parse import parser
+ 
+-
+ class ParserTest(unittest.TestCase):
+   """Tests |parser.Parse()|."""
+ 
+@@ -1375,6 +1371,5 @@ class ParserTest(unittest.TestCase):
+         r" *associated\? MyInterface& a;$"):
+       parser.Parse(source3, "my_file.mojom")
+ 
+-
+ if __name__ == "__main__":
+   unittest.main()
+diff --git a/qtwebengine/src/3rdparty/chromium/third_party/six/src/six.py b/qtwebengine/src/3rdparty/chromium/third_party/six/src/six.py
+index 5fe9f8e141..96b06f8ce7 100644
+--- a/qtwebengine/src/3rdparty/chromium/third_party/six/src/six.py
++++ b/qtwebengine/src/3rdparty/chromium/third_party/six/src/six.py
+@@ -71,6 +71,11 @@ else:
+             MAXSIZE = int((1 << 63) - 1)
+         del X
+ 
++if PY34:
++    from importlib.util import spec_from_loader
++else:
++    spec_from_loader = None
++
+ 
+ def _add_doc(func, doc):
+     """Add documentation to a function."""
+@@ -186,6 +191,11 @@ class _SixMetaPathImporter(object):
+             return self
+         return None
+ 
++    def find_spec(self, fullname, path, target=None):
++        if fullname in self.known_modules:
++            return spec_from_loader(fullname, self)
++        return None
++
+     def __get_module(self, fullname):
+         try:
+             return self.known_modules[fullname]
+@@ -223,6 +233,12 @@ class _SixMetaPathImporter(object):
+         return None
+     get_source = get_code  # same as get_code
+ 
++    def create_module(self, spec):
++        return self.load_module(spec.name)
++
++    def exec_module(self, module):
++        pass
++
+ _importer = _SixMetaPathImporter(__name__)
+ 
+ 
+-- 
+2.43.0
+
diff --git a/extras/packaging/gnu-linux/rules/debian-qt/patches/0002-fix-binary-tokenizer.patch b/extras/packaging/gnu-linux/rules/debian-qt/patches/0002-fix-binary-tokenizer.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c1128701dd6dbb414984eb367e355ace634e63ed
--- /dev/null
+++ b/extras/packaging/gnu-linux/rules/debian-qt/patches/0002-fix-binary-tokenizer.patch
@@ -0,0 +1,16 @@
+ qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp b/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
+index 3488120543..120e47a76f 100644
+--- a/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
++++ b/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
+@@ -472,7 +472,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
+     }
+     catch (const DeadlyImportError& e)
+     {
+-        if (!is64bits && (length > std::numeric_limits<std::uint32_t>::max())) {
++        if (!is64bits && (length > std::numeric_limits<uint32_t>::max())) {
+             throw DeadlyImportError("The FBX file is invalid. This may be because the content is too big for this older version (", ai_to_string(version), ") of the FBX format. (", e.what(), ")");
+         }
+         throw;
diff --git a/extras/packaging/gnu-linux/rules/debian-qt/patches/series b/extras/packaging/gnu-linux/rules/debian-qt/patches/series
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..edaeb51cd695b15f04c2e5972c10571d491bb5fb 100644
--- a/extras/packaging/gnu-linux/rules/debian-qt/patches/series
+++ b/extras/packaging/gnu-linux/rules/debian-qt/patches/series
@@ -0,0 +1,2 @@
+0001-replace_imp_by_importlib.patch
+0002-fix-binary-tokenizer.patch
\ No newline at end of file
diff --git a/extras/packaging/gnu-linux/rules/debian/jami-all.postinst b/extras/packaging/gnu-linux/rules/debian/jami-all.postinst
index 7457544f1ba11a4fb2cf40ca2f455e998e0654a8..9b25ca627c5687daadae3c3fef396bbb70472651 100755
--- a/extras/packaging/gnu-linux/rules/debian/jami-all.postinst
+++ b/extras/packaging/gnu-linux/rules/debian/jami-all.postinst
@@ -103,6 +103,8 @@ if [ -f /etc/os-release ]; then
         ENDTAG="ubuntu_23.04"
     elif [ "${UBUNTU_CODENAME}" = "mantic" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_23.10" ]; then
         ENDTAG="ubuntu_23.10"
+    elif [ "${UBUNTU_CODENAME}" = "noble" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_24.04" ]; then
+        ENDTAG="ubuntu_24.04"
     elif [ "${ID}" = "debian" ] && \
              [ "$(command -v lsb_release)" ] && \
              [ "$(lsb_release -rs)" = "testing" ]; then
diff --git a/extras/packaging/gnu-linux/scripts/build-package-debian.sh b/extras/packaging/gnu-linux/scripts/build-package-debian.sh
index 8cc1be4aa5933d59e4d6d11c3677e4d46a4628c8..3650f841f8b795389012ef06703295c57929e172 100755
--- a/extras/packaging/gnu-linux/scripts/build-package-debian.sh
+++ b/extras/packaging/gnu-linux/scripts/build-package-debian.sh
@@ -44,6 +44,23 @@ QUILT_REFRESH_ARGS="-p 1"
 
 if [ ! -f "${qt_deb_path}" ] || [ "${FORCE_REBUILD_QT}" = "true" ]; then
     (
+
+        # HACK: For now on ubuntu 24.04 there is no python3.10 package
+        # So create a PyEnv environment to install the required packages
+        if cat /etc/os-release | grep -Eq "24.04"; then
+            apt-get install git gcc make python3-pip libssl-dev curl libreadline-dev -y
+            curl https://pyenv.run | bash
+            export PYENV_ROOT="$HOME/.pyenv"
+            [[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
+            eval "$(pyenv init -)"
+            pyenv install 3.10.0
+            pyenv local 3.10.0
+
+            python -m pip install html5lib
+            python -m pip install six
+        fi
+
+
         flock 9                 # block until the lock file is gone
         test -f "${qt_deb_path}" && exit 0 # check again