diff --git a/README.md b/README.md
index 92d94c92fd58dcce25acac87c623c93a2f238d96..9fd089ad8b68ea76848bfbf2bd5bb7849a0f22f3 100644
--- a/README.md
+++ b/README.md
@@ -234,6 +234,24 @@ In addition to a textual report in the console, this job produces the following
 | -------------- | ---------------------------------------------------------------------------- | ----------------- |
 | `$PYTHON_PROJECT_DIR/reports/py-trivy.trivy.json` | [JSON](https://aquasecurity.github.io/trivy/latest/docs/vulnerability/examples/report/#json) | [DefectDojo integration](https://defectdojo.github.io/django-DefectDojo/integrations/parsers/#trivy)<br/>_This report is generated only if DefectDojo template is detected_ |
 
+### `py-sbom` job
+
+This job generates a [SBOM](https://cyclonedx.org/) file listing all dependencies using [syft](https://github.com/anchore/syft).
+
+It is bound to the `test` stage, and uses the following variables:
+
+| Name                  | description                            | default value     |
+| --------------------- | -------------------------------------- | ----------------- |
+| `PYTHON_SBOM_DISABLED` | Set to `true` to disable this job | _none_ |
+| `PYTHON_SBOM_SYFT_URL` | Url to the `tar.gz` package for `linux_amd64` of Syft to use (ex: `https://github.com/anchore/syft/releases/download/v0.62.3/syft_0.62.3_linux_amd64.tar.gz`)<br/>_When unset, the latest version will be used_ | _none_ |
+| `PYTHON_SBOM_OPTS` | Options for syft used for SBOM analysis | `--catalogers python-index-cataloger` |
+
+In addition to logs in the console, this job produces the following reports, kept for one week:
+
+| Report         | Format                                                                       | Usage             |
+| -------------- | ---------------------------------------------------------------------------- | ----------------- |
+| `$PYTHON_PROJECT_DIR/reports/py-sbom.cyclonedx.json` | [CycloneDX JSON](https://cyclonedx.org/docs/latest/json/) | [Security & Compliance integration](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportscyclonedx) |
+
 ### SonarQube analysis
 
 If you're using the SonarQube template to analyse your Python code, here is a sample `sonar-project.properties` file:
diff --git a/kicker.json b/kicker.json
index af096f654b8191356f1aaf154595127213aa6422..9c36e8d946f65e57ef2cc851b316baba7be78a3e 100644
--- a/kicker.json
+++ b/kicker.json
@@ -150,6 +150,25 @@
         }
       ]
     },
+    {
+      "id": "sbom",
+      "name": "Software Bill of Materials",
+      "description": "This job generates a file listing all dependencies using [syft](https://github.com/anchore/syft)",
+      "disable_with": "PYTHON_SBOM_DISABLED",
+      "variables": [
+        {
+          "name": "PYTHON_SBOM_SYFT_URL",
+          "description": "Url to the `tar.gz` package for `linux_amd64` of Syft to use\n\n_When unset, the latest version will be used_",
+          "advanced": true
+        },
+        {
+          "name": "PYTHON_SBOM_OPTS",
+          "description": "Options for syft used for SBOM analysis",
+          "default": "--catalogers python-index-cataloger",
+          "advanced": true
+        }
+      ]
+    },
     {
       "id": "release",
       "name": "Release",
diff --git a/templates/gitlab-ci-python.yml b/templates/gitlab-ci-python.yml
index a10b0d919b6358961d4dff3fd8b0d4a8c57fd07a..87a295e2b5bec1fd9ed778eec3047f30c417b1f2 100644
--- a/templates/gitlab-ci-python.yml
+++ b/templates/gitlab-ci-python.yml
@@ -76,6 +76,8 @@ variables:
   PYTHON_TRIVY_IMAGE: aquasec/trivy:latest
   PYTHON_TRIVY_ARGS: "--vuln-type library"
 
+  PYTHON_SBOM_OPTS: "--catalogers python-index-cataloger"
+
   PYTHON_RELEASE_NEXT: "minor"
 
   # By default, publish on the Packages registry of the project
@@ -835,6 +837,50 @@ py-trivy:
       when: never
     - !reference [.test-policy, rules]
 
+py-sbom:
+  extends: .python-base
+  stage: test
+  # force no dependency
+  dependencies: []
+  needs: []
+  script:
+    - mkdir -p -m 777 reports
+    - install_requirements
+    - |
+      case "$PYTHON_BUILD_SYSTEM" in
+        setuptools* | reqfile)
+          _pip freeze > "${PYTHON_REQS_FILE}"
+          ;;
+      esac
+    - |
+      if [[ -z "$PYTHON_SBOM_SYFT_URL" ]]
+      then
+        log_info "Syft version unset: retrieve latest version..."
+        PYTHON_SBOM_SYFT_URL=$(curl -sSf "https://api.github.com/repos/anchore/syft/releases?per_page=1" | \
+          python3 -c 'import json,sys;resp=json.load(sys.stdin);print(next(filter(lambda a: a["browser_download_url"].endswith("_linux_amd64.tar.gz"),resp[0]["assets"]))["browser_download_url"]);')
+        log_info "... use latest Syft version: \\e[32m$PYTHON_SBOM_SYFT_URL\\e[0m"
+      fi
+      python_sbom_syft="$PIP_CACHE_DIR/syft-$(echo "$PYTHON_SBOM_SYFT_URL" | md5sum | cut -d" " -f1)"
+      if [ ! -f $python_sbom_syft ]; then
+        wget -q -O syft.tar.gz $PYTHON_SBOM_SYFT_URL
+        tar zxf syft.tar.gz syft
+        mkdir -p $PIP_CACHE_DIR
+        mv ./syft $python_sbom_syft
+      fi
+    - $python_sbom_syft dir:${PYTHON_PROJECT_DIR} $PYTHON_SBOM_OPTS -o cyclonedx-json > reports/py-sbom.cyclonedx.json
+    - chmod a+r reports/py-sbom.cyclonedx.json
+  artifacts:
+    name: "Python SBOM from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
+    expire_in: 1 week
+    when: always
+    paths:
+      - "$PYTHON_PROJECT_DIR/reports/py-sbom.cyclonedx.json"
+  rules:
+    # exclude if disabled
+    - if: '$PYTHON_SBOM_DISABLED == "true"'
+      when: never
+    - !reference [.test-policy, rules]
+
 # (manual from master branch): triggers a release (tag creation)
 py-release:
   extends: .python-base