diff --git a/machines/nixos/web03/django-apps/annuaire.nix b/machines/nixos/web03/django-apps/annuaire.nix
index 54f59e5..86ce4a2 100644
--- a/machines/nixos/web03/django-apps/annuaire.nix
+++ b/machines/nixos/web03/django-apps/annuaire.nix
@@ -2,16 +2,7 @@
 #
 # SPDX-License-Identifier: EUPL-1.2
 
-{
-  pkgs,
-  sources,
-  config,
-  ...
-}:
-
-let
-  nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
-in
+{ config, ... }:
 
 {
   services.django-apps.sites.annuaire = {
@@ -26,15 +17,19 @@ in
 
     webHookSecret = config.age.secrets."webhook-annuaire_token".path;
 
-    python = pkgs.python3.override {
-      packageOverrides = _: _: { inherit (nix-pkgs) authens loadcredential; };
-    };
+    overlays.nix-pkgs = [
+      "authens"
+      "loadcredential"
+
+      # Dependencies
+      "python-cas"
+    ];
 
     dependencies = ps: [
-      ps.django
-      ps.pillow
-      ps.loadcredential
       ps.authens
+      ps.django
+      ps.loadcredential
+      ps.pillow
       ps.python-dateutil
     ];
 
diff --git a/machines/nixos/web03/django-apps/bocal.nix b/machines/nixos/web03/django-apps/bocal.nix
index d3d5300..59dbdb9 100644
--- a/machines/nixos/web03/django-apps/bocal.nix
+++ b/machines/nixos/web03/django-apps/bocal.nix
@@ -2,16 +2,7 @@
 #
 # SPDX-License-Identifier: EUPL-1.2
 
-{
-  pkgs,
-  sources,
-  config,
-  ...
-}:
-
-let
-  nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
-in
+{ config, ... }:
 
 {
   services.django-apps.sites.bocal = {
@@ -26,9 +17,14 @@ in
 
     webHookSecret = config.age.secrets."webhook-bocal_token".path;
 
-    python = pkgs.python3.override {
-      packageOverrides = _: _: { inherit (nix-pkgs) django-cas-ng django-solo loadcredential; };
-    };
+    overlays.nix-pkgs = [
+      "django-cas-ng"
+      "django-solo"
+      "loadcredential"
+
+      # Dependencies
+      "python-cas"
+    ];
 
     dependencies = ps: [
       ps.django
diff --git a/machines/nixos/web03/django-apps/ernestophone.nix b/machines/nixos/web03/django-apps/ernestophone.nix
index 60401df..670d652 100644
--- a/machines/nixos/web03/django-apps/ernestophone.nix
+++ b/machines/nixos/web03/django-apps/ernestophone.nix
@@ -2,16 +2,7 @@
 #
 # SPDX-License-Identifier: EUPL-1.2
 
-{
-  pkgs,
-  sources,
-  config,
-  ...
-}:
-
-let
-  nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
-in
+{ config, ... }:
 
 {
   services.django-apps.sites.ernestophone = {
@@ -31,16 +22,12 @@ in
 
     webHookSecret = config.age.secrets."webhook-ernestophone_token".path;
 
-    python = pkgs.python3.override {
-      packageOverrides = _: _: {
-        inherit (nix-pkgs)
-          django-avatar
-          django-cas-ng
-          django-solo
-          loadcredential
-          ;
-      };
-    };
+    overlays.nix-pkgs = [
+      "django-avatar"
+      "django-cas-ng"
+      "django-solo"
+      "loadcredential"
+    ];
 
     dependencies = ps: [
       ps.django
diff --git a/machines/nixos/web03/django-apps/gestiobds.nix b/machines/nixos/web03/django-apps/gestiobds.nix
index bd4f44c..e83b0af 100644
--- a/machines/nixos/web03/django-apps/gestiobds.nix
+++ b/machines/nixos/web03/django-apps/gestiobds.nix
@@ -2,16 +2,7 @@
 #
 # SPDX-License-Identifier: EUPL-1.2
 
-{
-  pkgs,
-  sources,
-  config,
-  ...
-}:
-
-let
-  inherit (import "${sources.nix-pkgs}/overlay.nix") mkOverlay;
-in
+{ config, ... }:
 
 {
   services.django-apps.sites.gestiobds = {
@@ -26,21 +17,16 @@ in
 
     webHookSecret = config.age.secrets."webhook-gestiobds_token".path;
 
-    python = pkgs.python3.override {
-      packageOverrides = mkOverlay {
-        folder = "python-modules";
-        plist = [
-          # Required packages
-          "authens"
-          "django-bootstrap-form"
-          "django-cas-ng"
-          "loadcredential"
+    overlays.nix-pkgs = [
+      # Required packages
+      "authens"
+      "django-bootstrap-form"
+      "django-cas-ng"
+      "loadcredential"
 
-          # Dependencies
-          "python-cas"
-        ];
-      };
-    };
+      # Dependencies
+      "python-cas"
+    ];
 
     dependencies = ps: [
       ps.authens
diff --git a/machines/nixos/web03/django-apps/gestiocof.nix b/machines/nixos/web03/django-apps/gestiocof.nix
index 67c1c20..2fc6881 100644
--- a/machines/nixos/web03/django-apps/gestiocof.nix
+++ b/machines/nixos/web03/django-apps/gestiocof.nix
@@ -2,16 +2,7 @@
 #
 # SPDX-License-Identifier: EUPL-1.2
 
-{
-  pkgs,
-  sources,
-  config,
-  ...
-}:
-
-let
-  inherit (import "${sources.nix-pkgs}/overlay.nix") mkOverlay;
-in
+{ config, ... }:
 
 {
   services.django-apps.sites.gestiocof = {
@@ -26,33 +17,25 @@ in
 
     webHookSecret = config.age.secrets."webhook-gestiocof_token".path;
 
-    python = pkgs.python3.override {
-      packageOverrides =
-        self: super:
-        (
-          (mkOverlay {
-            folder = "python-modules";
-            plist = [
-              # Required packages
-              "authens"
-              "django-bootstrap-form"
-              "django-cas-ng"
-              "loadcredential"
+    overlays = {
+      kat-pkgs = [
+        "django-djconfig"
+        "django-hCaptcha"
+        "wagtail-modeltranslation"
+        "wagtailmenus"
+        "django-cogwheels"
+      ];
 
-              # Dependencies
-              "python-cas"
-            ];
-          })
-          self
-          super
-        )
-        // (super.lib.genAttrs [
-          "django-djconfig"
-          "django-hCaptcha"
-          "wagtail-modeltranslation"
-          "wagtailmenus"
-          "django-cogwheels"
-        ] (name: self.callPackage "${sources.kat-pkgs}/python-pkgs/${name}.nix" { }));
+      nix-pkgs = [
+        # Required packages
+        "authens"
+        "django-bootstrap-form"
+        "django-cas-ng"
+        "loadcredential"
+
+        # Dependencies
+        "python-cas"
+      ];
     };
 
     dependencies = ps: [
diff --git a/machines/nixos/web03/django-apps/gestiojeux.nix b/machines/nixos/web03/django-apps/gestiojeux.nix
index 642f028..c367a4c 100644
--- a/machines/nixos/web03/django-apps/gestiojeux.nix
+++ b/machines/nixos/web03/django-apps/gestiojeux.nix
@@ -2,16 +2,7 @@
 #
 # SPDX-License-Identifier: EUPL-1.2
 
-{
-  pkgs,
-  sources,
-  config,
-  ...
-}:
-
-let
-  nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
-in
+{ config, ... }:
 
 {
   services.django-apps.sites.gestiojeux = {
@@ -31,16 +22,15 @@ in
       module = "gestiojeux";
     };
 
-    python = pkgs.python3.override {
-      packageOverrides = _: _: {
-        inherit (nix-pkgs)
-          django-autoslug
-          django-cas-ng
-          loadcredential
-          markdown-icons
-          ;
-      };
-    };
+    overlays.nix-pkgs = [
+      "django-autoslug"
+      "django-cas-ng"
+      "loadcredential"
+      "markdown-icons"
+
+      # Dependencies
+      "python-cas"
+    ];
 
     django = ps: ps.django_4;
     dependencies = ps: [
diff --git a/machines/nixos/web03/django-apps/interludes.nix b/machines/nixos/web03/django-apps/interludes.nix
index 61730e4..c8d51f9 100644
--- a/machines/nixos/web03/django-apps/interludes.nix
+++ b/machines/nixos/web03/django-apps/interludes.nix
@@ -2,16 +2,7 @@
 #
 # SPDX-License-Identifier: EUPL-1.2
 
-{
-  config,
-  pkgs,
-  sources,
-  ...
-}:
-
-let
-  nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
-in
+{ config, ... }:
 
 {
   services.webhook.extraArgs = [ "-debug" ];
@@ -36,9 +27,10 @@ in
 
     dbType = "sqlite";
 
-    python = pkgs.python3.override {
-      packageOverrides = _: _: { inherit (nix-pkgs) python-cas loadcredential; };
-    };
+    overlays.nix-pkgs = [
+      "loadcredential"
+      "python-cas"
+    ];
 
     django = ps: ps.django_4;
     dependencies = ps: [
diff --git a/modules/nixos/django-apps/default.nix b/modules/nixos/django-apps/default.nix
index 550c9ee..d2e5b35 100644
--- a/modules/nixos/django-apps/default.nix
+++ b/modules/nixos/django-apps/default.nix
@@ -8,12 +8,14 @@
   options,
   pkgs,
   utils,
+  sources,
   ...
 }:
 
 let
   inherit (lib)
     attrNames
+    composeManyExtensions
     concatLists
     concatMapAttrs
     filterAttrs
@@ -30,6 +32,7 @@ let
     mkPackageOption
     nameValuePair
     optional
+    optionalString
     optionals
     recursiveUpdate
     toUpper
@@ -42,6 +45,7 @@ let
     enum
     functionTo
     ints
+    lines
     listOf
     nullOr
     package
@@ -192,6 +196,24 @@ in
 
               python = mkPackageOption pkgs "python3" { };
 
+              overlays = {
+                kat-pkgs = mkOption {
+                  type = listOf str;
+                  default = [ ];
+                  description = ''
+                    List of python packages to pull from [kat-pkgs](https://git.dgnum.eu/lbailly/kat-pkgs).
+                  '';
+                };
+
+                nix-pkgs = mkOption {
+                  type = listOf str;
+                  default = [ ];
+                  description = ''
+                    List of python packages to pull from [nix-pkgs](https://git.hubrecht.ovh/hubrecht/nix-pkgs).
+                  '';
+                };
+              };
+
               django = mkOption {
                 type = functionTo package;
                 default = ps: ps.django;
@@ -203,14 +225,38 @@ in
 
               djangoEnv = mkOption {
                 type = package;
-                default = config.python.withPackages (
-                  ps:
-                  [ (config.django ps) ]
-                  ++ (optional (config.application.type != "daphne") ps.gunicorn)
-                  ++ (optional (config.application.type == "asgi") ps.uvicorn)
-                  ++ (optional (config.dbType == "postgresql") ps.psycopg)
-                  ++ (config.dependencies ps)
-                );
+                default =
+                  let
+                    overlays =
+                      (optional (config.overlays.nix-pkgs != [ ]) (
+                        (import "${sources.nix-pkgs}/overlay.nix").mkOverlay {
+                          folder = "python-modules";
+                          plist = config.overlays.nix-pkgs;
+                        }
+                      ))
+                      ++ (optional (config.overlays.kat-pkgs != [ ]) (
+                        self: super:
+                        super.lib.genAttrs config.overlays.kat-pkgs (
+                          name: self.callPackage "${sources.kat-pkgs}/python-pkgs/${name}.nix" { }
+                        )
+                      ));
+                  in
+                  (
+                    if (overlays != [ ]) then
+                      config.python.override {
+                        packageOverrides = composeManyExtensions overlays;
+                      }
+                    else
+                      config.python
+                  ).withPackages
+                    (
+                      ps:
+                      [ (config.django ps) ]
+                      ++ (optional (config.application.type != "daphne") ps.gunicorn)
+                      ++ (optional (config.application.type == "asgi") ps.uvicorn)
+                      ++ (optional (config.dbType == "postgresql") ps.psycopg)
+                      ++ (config.dependencies ps)
+                    );
                 description = ''
                   The python version used to run the app, with the correct dependencies.
                 '';
@@ -303,6 +349,7 @@ in
                     git pull
                     python3 ${config.managePath} migrate
                     python3 ${config.managePath} collectstatic --no-input
+                    ${optionalString (config.extraUpdateSteps != null) config.extraUpdateSteps}
                   '';
                 };
                 description = ''
@@ -310,6 +357,15 @@ in
                 '';
               };
 
+              extraUpdateSteps = mkOption {
+                type = nullOr lines;
+                default = null;
+                description = ''
+                  Steps taken during the update after the migration is done
+                  and the static files have been collected.
+                '';
+              };
+
               webHookSecret = mkOption {
                 type = path;
                 description = ''
@@ -425,7 +481,7 @@ in
                 trigger-rule = {
                   and = [
                     {
-                      or = [
+                      "or" = [
                         {
                           match = {
                             type = "payload-hmac-sha256";