diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 10d41ac7bc2730597c0bfea7d67561fc7db29804..b4f5865682e1f6afe3222f6c076795a2259b358d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -550,3 +550,28 @@ build-npm-cspell:
   extends: .build-with-kaniko
   variables:
     CONTAINER_PATH: npm-cspell
+
+.oe-selftest:
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "schedule"'
+      variables:
+        CI_ONIRO_MANIFEST_URL: "$CI_PROJECT_URL"
+        CI_ONIRO_MANIFEST_BRANCH: "$CI_COMMIT_REF_NAME"
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE =~ /^(wip|draft):.*/i'
+      when: manual
+      variables:
+        CI_ONIRO_MANIFEST_URL: "$CI_MERGE_REQUEST_SOURCE_PROJECT_URL"
+        CI_ONIRO_MANIFEST_BRANCH: "$CI_COMMIT_REF_NAME"
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+      # TODO(Chase): only trigger the test when manifests changed.
+      # changes:
+      #   - manifests/*.xml
+      variables:
+        CI_ONIRO_MANIFEST_URL: "$CI_MERGE_REQUEST_SOURCE_PROJECT_URL"
+        CI_ONIRO_MANIFEST_BRANCH: "$CI_COMMIT_REF_NAME"
+
+oe-selftest-gcc:
+  extends: .oe-selftest
+  timeout: 10h
+  variables:
+    CI_ONIRO_OE_SEFLTESTS: gcc
diff --git a/.oniro-ci/build-generic.yaml b/.oniro-ci/build-generic.yaml
index 67abc4ad1cf53831e130d9b753b15cbc8b932e16..981adf57fa0b885983611b5efe3876710452a130 100644
--- a/.oniro-ci/build-generic.yaml
+++ b/.oniro-ci/build-generic.yaml
@@ -451,3 +451,33 @@
         # Run this job in case the pipeline changes.
         - .oniro-ci/*.yml
         - .gitlab-ci.yml
+
+# This job is documented in docs/ci/hidden-jobs/oe-selftest.rst
+.oe-selftest:
+  image:
+    # TODO(Chase): move to registry.ostc-eu.org/ostc/oniro/oe-selftest:latest
+    # once it is available.
+    name: chaseqi/bitbake-builder:v1.3
+  tags: [qemu-friendly, large-disk, $CI_ONIRO_RUNNER_TAG, $CI_ONIRO_INSTANCE_SIZE]
+  stage: test
+  extends: .bitbake-workspace
+  variables:
+    MACHINE: qemux86-64
+    CI_ONIRO_BUILD_FLAVOUR: linux
+    CI_ONIRO_BB_LOCAL_CONF_SANITY_TESTED_DISTROS: ""
+    CI_ONIRO_OE_SEFLTESTS: ""
+    CI_ONIRO_OE_SEFLTEST_SKIPS: ""
+  script:
+    - test -n "$CI_ONIRO_OE_SEFLTESTS" || (
+        echo "nothing to test - CI_ONIRO_OE_SEFLTESTS is empty"
+        && exit 1 )
+    - !reference [.bitbake-workspace, script]
+    # The USER variable is required by oe-selftest but missing in env.
+    - export USER=builder
+    - time oe-selftest -vvv -r $CI_ONIRO_OE_SEFLTESTS -K
+    - mkdir -p "$CI_PROJECT_DIR"/artifacts && rm -rf "$CI_PROJECT_DIR"/artifacts/*
+    - find ../build-st \( -name "*.sum" -o -name "*.log" \) \( -path "*/gcc-runtime/*testsuite*" \) -exec cp --verbose {} "$CI_PROJECT_DIR"/artifacts/ \;
+    - rm -rf ../build-st
+  artifacts:
+    paths:
+      - artifacts/
diff --git a/docs/ci/hidden-jobs/index.rst b/docs/ci/hidden-jobs/index.rst
index 9248b2d53486b0eb2cb25008610321803e40d064..e809813891f479f6305736cb5e5502ac53c1f450 100644
--- a/docs/ci/hidden-jobs/index.rst
+++ b/docs/ci/hidden-jobs/index.rst
@@ -30,3 +30,4 @@ implementation details.
    lava-report
    aggregate-docs
    publish-rauc-bundle-to-hawkbit
+   oe-selftest
diff --git a/docs/ci/hidden-jobs/oe-selftest.rst b/docs/ci/hidden-jobs/oe-selftest.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d457335c7433f97ab1a75c2705287b044a093093
--- /dev/null
+++ b/docs/ci/hidden-jobs/oe-selftest.rst
@@ -0,0 +1,30 @@
+.. SPDX-FileCopyrightText: Huawei Inc.
+..
+.. SPDX-License-Identifier: CC-BY-4.0
+
+============
+.oe-selftest
+============
+
+The ``.oe-selftest`` job extends the :doc:`bitbake-workspace` job to configure
+OpenEmbedded Self test environment. The job does not run any tests by itself,
+actual tests should be run in the downstream test jobs.
+
+Job Variables
+=============
+
+Oe-selftest is based on Python unitest. Tests are orgnazied by module, class
+and method. The ``.oe-selftest`` job defines two variables as a way to
+customize what tests to run.
+
+CI_ONIRO_OE_SEFLTESTS
+---------------------
+
+The specific tests to run. The default value is empty. The order the tests are
+running is alphabetical.
+
+
+CI_ONIRO_OE_SEFLTEST_SKIPS
+--------------------------
+
+The specific tests to skip. The default value is empty.