diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9adf860632e8a2..8a70cde3d8bc13 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -231,10 +231,14 @@ jobs:
     name: >-
       Ubuntu
       ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
+      ${{ fromJSON(matrix.bolt) && '(bolt)' || '' }}
     needs: check_source
     if: needs.check_source.outputs.run_tests == 'true'
     strategy:
       matrix:
+        bolt:
+        - false
+        - true
         free-threading:
         - false
         - true
@@ -246,9 +250,16 @@ jobs:
         exclude:
         - os: ubuntu-24.04-aarch64
           is-fork: true
+        # Do not test BOLT with free-threading, to conserve resources
+        - bolt: true
+          free-threading: true
+        # BOLT currently crashes during instrumentation on aarch64
+        - os: ubuntu-24.04-aarch64
+          bolt: true
     uses: ./.github/workflows/reusable-ubuntu.yml
     with:
       config_hash: ${{ needs.check_source.outputs.config_hash }}
+      bolt-optimizations: ${{ matrix.bolt }}
       free-threading: ${{ matrix.free-threading }}
       os: ${{ matrix.os }}
 
diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml
index 46c542940c8483..686e8fe1abc980 100644
--- a/.github/workflows/reusable-ubuntu.yml
+++ b/.github/workflows/reusable-ubuntu.yml
@@ -6,6 +6,11 @@ on:
       config_hash:
         required: true
         type: string
+      bolt-optimizations:
+        description: Whether to enable BOLT optimizations
+        required: false
+        type: boolean
+        default: false
       free-threading:
         description: Whether to use free-threaded mode
         required: false
@@ -34,6 +39,12 @@ jobs:
       run: echo "::add-matcher::.github/problem-matchers/gcc.json"
     - name: Install dependencies
       run: sudo ./.github/workflows/posix-deps-apt.sh
+    - name: Install Clang and BOLT
+      if: ${{ fromJSON(inputs.bolt-optimizations) }}
+      run: |
+        sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 19
+        sudo apt-get install bolt-19
+        echo PATH="$(llvm-config-19 --bindir):$PATH" >> $GITHUB_ENV
     - name: Configure OpenSSL env vars
       run: |
         echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV"
@@ -73,7 +84,10 @@ jobs:
         key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}
     - name: Configure CPython out-of-tree
       working-directory: ${{ env.CPYTHON_BUILDDIR }}
+      # `test_unpickle_module_race` writes to the source directory, which is
+      # read-only during builds — so we exclude it from profiling with BOLT.
       run: >-
+        PROFILE_TASK='-m test --pgo --ignore test_unpickle_module_race'
         ../cpython-ro-srcdir/configure
         --config-cache
         --with-pydebug
@@ -81,6 +95,7 @@ jobs:
         --enable-safety
         --with-openssl="$OPENSSL_DIR"
         ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
+        ${{ fromJSON(inputs.bolt-optimizations) && '--enable-bolt' || '' }}
     - name: Build CPython out-of-tree
       if: ${{ inputs.free-threading }}
       working-directory: ${{ env.CPYTHON_BUILDDIR }}
diff --git a/Lib/test/test_perf_profiler.py b/Lib/test/test_perf_profiler.py
index 1e74990878007a..6f1fd8d38e4ea0 100644
--- a/Lib/test/test_perf_profiler.py
+++ b/Lib/test/test_perf_profiler.py
@@ -47,6 +47,7 @@ def tearDown(self) -> None:
         for file in files_to_delete:
             file.unlink()
 
+    @unittest.skipIf(support.check_bolt_optimized, "fails on BOLT instrumented binaries")
     def test_trampoline_works(self):
         code = """if 1:
                 def foo():
@@ -100,6 +101,7 @@ def baz():
                 "Address should contain only hex characters",
             )
 
+    @unittest.skipIf(support.check_bolt_optimized, "fails on BOLT instrumented binaries")
     def test_trampoline_works_with_forks(self):
         code = """if 1:
                 import os, sys
@@ -160,6 +162,7 @@ def baz():
         self.assertIn(f"py::bar_fork:{script}", child_perf_file_contents)
         self.assertIn(f"py::baz_fork:{script}", child_perf_file_contents)
 
+    @unittest.skipIf(support.check_bolt_optimized, "fails on BOLT instrumented binaries")
     def test_sys_api(self):
         code = """if 1:
                 import sys