diff --git a/src/pages/about/blog/python-autograder-v2/index.mdx b/src/pages/about/blog/python-autograder-v2/index.mdx
new file mode 100644
index 0000000..ee964fe
--- /dev/null
+++ b/src/pages/about/blog/python-autograder-v2/index.mdx
@@ -0,0 +1,146 @@
+import { BlogMarkdownLayout, BlogCalloutBox } from "../../../../components/BlogMarkdownLayout";
+
+export const meta = {
+ title: "Introducing the new Python autograder (beta)",
+ date: "2026-05-22T00:00:00-06:00",
+ author: "Eliot Robson",
+ tags: ["Technical", "Autograding"],
+ excerpt:
+ "A new pytest-based Python autograder is now in beta, bringing sandboxed execution, student-friendly feedback, and a lightweight image built for intro CS courses.",
+};
+
+# Introducing the new Python autograder (beta)
+
+We're excited to announce a public beta of the new Python autograder for PrairieLearn: [`pytest-prairielearn-grader`](https://github.com/eliotwrobson/pl-python-autograder-v2). It's a ground-up rewrite built on [pytest](https://pytest.org/), and it ships with a set of features that support reliable graders and give students clear, actionable feedback.
+
+## Why a new autograder?
+
+The new autograder offers instructors a few key capabilities:
+
+- **Security isolation.** Student code now runs in an isolated subprocess, protecting the grading harness and keeping grading behavior reliable.
+- **Clear feedback.** Friendly output mode and built-in assertions make student feedback easy to read, especially in intro courses.
+- **Centralized configuration.** Timeouts, import restrictions, and other settings can be managed cleanly with `ConfigObject`.
+- **Lightweight startup image.** Intro questions can use the `:lite` image (~60–80 MB) for quick cold starts and a compact dependency surface.
+
+## Key advantages for instructors
+
+### Sandboxed execution
+
+Student code runs in a separate subprocess via Unix sockets, completely isolated from the grader harness. This gives instructors reliable grading behavior with clear boundaries around imports, runtime behavior, and timeout enforcement.
+
+You can also control exactly which modules students are allowed to import:
+
+```python
+# server.py
+def generate(data):
+ # Only the standard library is available — no numpy, no os, no subprocess
+ data["params"]["import_whitelist"] = ["math", "statistics"]
+```
+
+### Student-friendly feedback
+
+By default, test failures show a Python exception name and a short message. For intro courses, you can switch to `"friendly"` output mode, which suppresses tracebacks entirely and shows only a clean, structured message:
+
+```
+Checking: add(2, 3)
+Expected output: 5
+Your code output: -1
+The expected and actual output do not match.
+```
+
+This pairs naturally with the built-in assertion helpers (`assert_fn_equal`, `assert_equal`, etc.) that automatically generate these messages.
+
+### Automatic ungradable detection
+
+If a student submits code with a `SyntaxError`, the grader marks the submission as **ungradable**. PrairieLearn's external grading framework then allows students to fix and resubmit without consuming a grading attempt, while still showing a clear error message. This is especially helpful in courses where students are still learning Python syntax.
+
+### A lightweight image for intro courses
+
+Two Docker images are published:
+
+| Image | Size | Includes |
+|-------|------|----------|
+| `:latest` | ~400–500 MB | Full scientific stack: numpy, pandas, scipy, matplotlib, sympy, scikit-learn, and more |
+| `:lite` | ~60–80 MB | Grader core only — no scientific libraries |
+
+For introductory CS courses where students use only the Python standard library, the `:lite` image is the right choice. Its lightweight footprint supports quick cold-start pulls and a reduced security surface.
+
+## A simple intro-course example
+
+Here's what a complete question setup looks like for an intro CS course using the `:lite` image.
+
+### `info.json`
+
+```json
+{
+ "title": "Implement add()",
+ "topic": "Functions",
+ "tags": ["intro", "functions"],
+ "type": "v3",
+ "gradingMethod": "External",
+ "externalGradingOptions": {
+ "enabled": true,
+ "image": "eliotwrobson/grader-python-pytest:lite",
+ "timeout": 30
+ }
+}
+```
+
+### `question.html`
+
+```html
+
+
+ Write a function add(a, b) that returns the sum of two numbers.
+
+
+
+
+def add(a, b):
+ # your code here
+ pass
+
+```
+
+### `tests/test_student.py`
+
+```python
+import pytest
+from pytest_prairielearn_grader import ConfigObject
+from pytest_prairielearn_grader.assertions import assert_fn_equal
+from pytest_prairielearn_grader.fixture import StudentFixture
+
+autograder_config = ConfigObject(
+ output_level="friendly",
+)
+
+
+@pytest.mark.grading_data(name="add(2, 3) returns 5", points=3)
+def test_add_basic(sandbox: StudentFixture) -> None:
+ assert_fn_equal(sandbox, "add", args=(2, 3), expected=5)
+
+
+@pytest.mark.grading_data(name="add(-1, 1) returns 0", points=3)
+def test_add_negative(sandbox: StudentFixture) -> None:
+ assert_fn_equal(sandbox, "add", args=(-1, 1), expected=0)
+
+
+@pytest.mark.grading_data(name="add(0, 0) returns 0", points=4)
+def test_add_zeros(sandbox: StudentFixture) -> None:
+ assert_fn_equal(sandbox, "add", args=(0, 0), expected=0)
+```
+
+When a student submits `return a - b` by mistake, they see:
+
+```
+Checking: add(2, 3)
+Expected output: 5
+Your code output: -1
+The expected and actual output do not match.
+```
+
+Students get concise, actionable feedback focused on what to fix next.
+
+
+The autograder is in **public beta**. Install the pytest plugin with `pip install pytest-prairielearn-grader` and follow the [quick start guide](https://github.com/eliotwrobson/pl-python-autograder-v2/blob/main/quick_start.md) to get set up locally. Feedback and bug reports are welcome on the [issue tracker](https://github.com/eliotwrobson/pl-python-autograder-v2/issues).
+