5 min read

nanochat: Train Your Own ChatGPT for About $100 (GitHub, Scanned)

Andrej Karpathy's minimal full-stack recipe to train a small ChatGPT-style model and chat with it.

nanochat logo
✅
Scan: safe. Nothing to warn about. A small, readable training codebase; the advisories are in libraries it uses on a training box, not in anything that serves the internet. Scanned Jul 3, 2026; the full report is below.

nanochat is the smallest complete pipeline for building a chat model from nothing: tokenizer training, pretraining, fine-tuning, evaluation and inference, in a compact, hackable PyTorch codebase. One script, runs/speedrun.sh, takes a rented 8xH100 node from empty to a GPT-2-grade model in about two hours, roughly $48 at $24 an hour, and you then talk to it from a command-line chat. A single --depth setting scales the model, and every other hyperparameter is derived from it.

Andrej Karpathy, a founding member of OpenAI and former head of AI at Tesla, released it in October 2025 as "the best ChatGPT that $100 can buy", and it went viral on X at launch; it has about 58,000 stars and is MIT-licensed. It is a teaching and research harness, not an assistant: the result chats like a small child, by the author's own description, and a GPT-2 speedrun leaderboard tracks community efforts to train faster.

Who it is for

Students and engineers who want to understand how chat models are actually trained end to end, and researchers who need a small, readable baseline to experiment on.

Getting started

1. Clone the repository

git clone https://github.com/karpathy/nanochat.git && cd nanochat

2. Install dependencies with uv for a CUDA GPU (use --extra cpu for CPU or Apple Silicon)

uv sync --extra gpu && source .venv/bin/activate

3. On an 8xH100 node, train the GPT-2-grade model (about 1.5 to 2 hours)

bash runs/speedrun.sh

4. Chat with your model

python -m scripts.chat_cli

The speedrun is designed for a rented 8xH100 (or 8xA100) node; a single GPU works but takes about eight times longer, and GPUs under 80 GB need a smaller batch size. runs/runcpu.sh trains a tiny model on a CPU or Mac in tens of minutes, with weak results.

Safety scan

We cloned karpathy/nanochat at commit 92d63d4 on Jul 3, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.

  • No secrets, no committed binaries and no bare-IP URLs across 53 files and about 12,400 lines, most of it Python you can read in an afternoon.
  • The three pattern hits are the same line in runs/speedrun.sh, runs/runcpu.sh and runs/miniseries.sh: if uv is missing, install it with Astral's official curl | sh installer. Downloads otherwise come from Hugging Face datasets and an evaluation bundle in Karpathy's public S3 bucket.
  • uv.lock pins 105 packages with 90 known advisories (1 critical, 58 high). Most are GitPython 3.1.45, which arrives with the Weights and Biases logger and is never pointed at an untrusted repository; the rest are in Pillow, protobuf, pyarrow and click. These matter little on a rented GPU node you throw away after training.
  • The scripts are meant to run on a dedicated 8xH100 box and use torchrun, so run them in a cloud instance or container rather than on a machine with your personal data.
  • No workflows and no tagged releases. Licence present; no security policy, Dependabot, CodeQL or contributing guide.

What the scanner counted

CheckResult
SecretsNone found.
Suspicious code3 pattern hits found and read; every one is listed under the raw findings.
Install-time code1 installer script (one fetches and runs a remote script)
Committed binariesNone.
CI workflowsNo GitHub Actions workflows.
Network hosts9 distinct hosts referenced from source; most often arxiv.org, huggingface.co, github.com, astral.sh. No URLs to bare IP addresses.
Known vulnerabilities90 advisories across 104 pinned packages: 1 critical, 58 high, 23 moderate, 8 low. uv.lock: 105 packages, 90 advisories.
Project hygieneHas licence file. Missing security policy, automated dependency updates, CodeQL, contributing guide.
OpenSSF ScorecardNot scored: the project is not in Scorecard's weekly index.

The raw findings

Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.

Pattern hits (3)
WhereRuleMatch
runs/miniseries.sh:15download-piped-to-shellcommand -v uv &> /dev/null || curl -LsSf https://astral.sh/uv/install.sh | sh
runs/runcpu.sh:16download-piped-to-shellcommand -v uv &> /dev/null || curl -LsSf https://astral.sh/uv/install.sh | sh
runs/speedrun.sh:22download-piped-to-shellcommand -v uv &> /dev/null || curl -LsSf https://astral.sh/uv/install.sh | sh
Installer scripts (1)
  • runs/runcpu.sh, 59 lines, fetches and runs a remote script; talks to astral.sh
Worst known vulnerabilities (24 of 90)
AdvisorySeverityPackageSummary
GHSA-284h-m62q-gf8wcriticalgitpython@3.1.45GitPython: Dormant multi-line git-config values are corrupted into live injected directives (e.g. core.hooksPath) on any…
GHSA-47fr-3ffg-hgmwhighclick@8.2.1
GHSA-239g-whfq-7xj9highgitpython@3.1.45GitPython: Repository content can impersonate the git directory, leading to arbitrary code execution
GHSA-2f96-g7mh-g2hxhighgitpython@3.1.45GitPython: Command Injection via git long-option prefix abbreviation bypass of CVE-2026-42215 blocklist
GHSA-3f7w-8rr8-f37fhighgitpython@3.1.45GitPython: Unguarded git option forwarding in IndexFile.checkout() and TagReference.create() enables arbitrary file over…
GHSA-3rp5-jjmw-4wv2highgitpython@3.1.45GitPython: git-config section-name injection enables arbitrary config directives (core.sshCommand RCE)
GHSA-4gmw-gg2m-w46phighgitpython@3.1.45GitPython: Unguarded git read-tree option forwarding in IndexFile.from_tree/reset/merge_tree enables arbitrary file over…
GHSA-6p8h-3wgx-97gfhighgitpython@3.1.45GitPython: Incomplete unsafe_git_clone_options denylist omits --template enabling arbitrary command execution via clone …
GHSA-7545-fcxq-7j24highgitpython@3.1.45GitPython reference APIs has a path traversal vulnerability that allows arbitrary file write and delete outside the repo…
GHSA-7833-fr7j-v32qhighgitpython@3.1.45GitPython: Arbitrary local file content disclosure via [include] directive in untrusted .gitmodules (SubmoduleConfigPars…
GHSA-8mcc-hrx5-hvxchighgitpython@3.1.45GitPython: clone_from()/clone() omit --separate-git-dir from unsafe_git_clone_options, enabling arbitrary git-directory …
GHSA-94p4-4cq8-9g67highgitpython@3.1.45GitPython: Environment-variable exfiltration via Repo.create_remote() / Remote.add() URL (incomplete fix of GHSA-rwj8-pg…
GHSA-956x-8gvw-wg5vhighgitpython@3.1.45GitPython: command injection via unguarded Git options in `Repo.archive()`, `git.ls_remote()`, and arbitrary file overwr…
GHSA-9rj7-rf2p-w77rhighgitpython@3.1.45GitPython: Unguarded git option forwarding in Repo.init enables arbitrary command execution via --template clone hooks
GHSA-fjr4-x663-mwxchighgitpython@3.1.45GitPython: Arbitrary file overwrite via git diff --output argument injection in Diffable.diff (key- and value-controlled…
GHSA-g5vv-9gxw-82hxhighgitpython@3.1.45GitPython: Denial of Service via catastrophic backtracking (ReDoS) in Actor.name_email_regex - commit author/committer f…
GHSA-hmq2-w58f-27jchighgitpython@3.1.45GitPython: Arbitrary Git Repository Creation Outside the Working Tree via Unvalidated .gitmodules Submodule Name in GitP…
GHSA-jm78-9fvv-mhgrhighgitpython@3.1.45GitPython: git-config OPTION-name injection via =/#/whitespace bypasses name validator, enabling forged core.sshCommand/…
GHSA-mv93-w799-cj2whighgitpython@3.1.45GitPython: Newline injection in config_writer() section parameter bypasses CVE-2026-42215 patch, enabling RCE via core.h…
GHSA-r9mr-m37c-5fr3highgitpython@3.1.45GitPython: Unsafe git option guard bypass via single-character kwarg value token smuggling enables arbitrary command exe…
GHSA-rpm5-65cw-6hj4highgitpython@3.1.45GitPython has Command Injection via Git options bypass
GHSA-rwj8-pgh3-r573highgitpython@3.1.45GitPython: Environment-variable exfiltration via os.path.expandvars() on Repo.clone_from() URL
GHSA-v87r-6q3f-2j67highgitpython@3.1.45GitPython: Newline injection in config_writer().set_value() enables RCE via core.hooksPath
GHSA-wvpp-8hx9-p66jhighgitpython@3.1.45GitPython: Unsafe git option guard bypass via split_single_char_options=False short-option token smuggling enables comma…

By the numbers

Stars58.4K
Forks8,179
Contributors57
Commits441
Open issues28
Open pull requests95
Releases0
Latest releasenone tagged
LicenceMIT
Main languagePython
Project age11 months
Last pushSep 7, 2026
Tracked files53
Lines of code12.4K
Checkout size1 MB

Lines by language: Python 7,811, Jupyter 2,565, Markdown 1,565, Shell 388, TOML 70.

Questions

Is nanochat free?

The code is MIT-licensed and free. The compute is the cost: the reference speedrun takes about two hours on an 8xH100 node, around $48 at typical rates or closer to $15 on spot instances, and bigger models scale up from there. The tiny CPU run costs nothing but produces a weak model.

How good is a model trained with nanochat?

About GPT-2 level, which today means it can hold a short, simple conversation, write a rough poem and get facts wrong often. Karpathy compares it to talking to a kindergartener. The value is in seeing and changing every stage of training, not in replacing a commercial assistant.

Can I run nanochat on my own computer?

You can run it on CPU or Apple Silicon with the runcpu.sh script, which shrinks the model so training finishes in tens of minutes. A single consumer NVIDIA GPU can run the real pipeline with a smaller batch size, but expect many hours or days.


This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.