This is episode fifteen of What Did I Just Install.
There is a word in Japanese. Jinja. Written with two kanji characters, it means a Shinto shrine, a temple. And there is a word in English. Template. And if you say them both out loud, temple and template, they rhyme. That is the entire naming story. A nineteen-year-old Austrian programmer thought it was funny, and now, with PyPI download statistics suggesting over eleven billion total downloads, his pun runs inside Ansible playbooks, data pipelines, documentation generators, and roughly half the Python web applications on earth.
The official documentation even includes a clarification. It is not named after the city in Uganda. Just the temple. Just the pun. But what started as a clever name for a student project became one of the most quietly essential pieces of software in the Python ecosystem. Jinja2 is everywhere, and yet almost nobody talks about it. It has no dramatic fork, no licensing war, no maintainer meltdown. It just works. And the person who built it was barely old enough to vote when he started.
Before we get to the person, let us talk about the problem. Imagine you are building a website. You have a page that shows a user's profile. The layout is the same for every user, the same header, the same sidebar, the same footer, but the name, the photo, the bio, those change. You could write the entire page in Python, concatenating strings of HTML together with plus signs and format calls, embedding angle brackets inside quote marks inside function arguments. People did this. It was exactly as miserable as it sounds.
A template engine solves this by letting you write the HTML separately, with little placeholders where the dynamic content goes. Double curly braces around a variable name. Percent signs around a loop or a conditional. The template looks almost like a normal web page, except it has these small holes punched in it, and the engine fills the holes with real data at runtime. You write the structure once. The data changes every time.
Django, the other major Python web framework, had its own template language from the beginning. It was deliberately limited. The Django philosophy was that templates should contain as little logic as possible, no function calls, no arbitrary Python expressions, just simple variable lookups and basic loops. This was a design choice rooted in the idea that designers, not programmers, would be editing templates, and designers should not have to understand Python.
Jinja2 took a different position. It gave templates real expressions, filters you could chain together, macros you could define and reuse, template inheritance where a child template could override specific blocks of a parent. It was still a template language, not full Python, but it was powerful enough that you could build complex page layouts without constantly escaping back to your application code. The philosophy was that the person writing templates is probably also the person writing the application, and artificial restrictions just make everyone's life harder.
Armin Ronacher was born on May tenth, nineteen eighty-nine, in Graz, Austria. He started programming around age thirteen, teaching himself QBasic the way many kids of that era did, by making small games and seeing what happened when you changed the numbers. By fourteen, he had moved to Delphi, and he and a collaborator built a game called Be a Bee. It won second place in the Prix Ars Electronica's under-nineteen computing category in two thousand three. He was fourteen years old, and he was already winning awards for software.
What happened next is one of those origin stories that only makes sense in open source. Ronacher and another young programmer named Georg Brandl discovered Python and decided they were going to build a replacement for phpBB, the bulletin board software that powered half the forums on the internet. Their project was going to be called Pocoo. The bulletin board never materialized. But in the process of trying to build it, Ronacher and Brandl created a series of tools that would reshape Python development for the next two decades.
Brandl went on to create Pygments, the syntax highlighting library, and co-create Sphinx, the documentation generator that powers nearly every Python project's documentation. Ronacher built Werkzeug, a toolkit for building web applications on top of WSGI, the protocol that connects Python web apps to web servers. And then he built Jinja. Not Jinja2 yet. The first version. A template engine that was better than what existed, but not yet what it would become. The full rewrite came in two thousand eight, when Ronacher was nineteen. That was Jinja2, the version the world actually uses, and it arrived with the features that made it dominant. Template inheritance, sandboxed execution, automatic HTML escaping to prevent security vulnerabilities, and a design that was fast enough for production use.
If you already listen to this series, you have heard Ronacher's name before. In episode ten, he appeared briefly as the Flask creator who pushed back on PyPI's two-factor authentication mandate. But that one sentence does not begin to capture what this person built. Because Jinja2 was just one piece of something larger.
On April first, two thousand ten, Armin Ronacher posted what appeared to be an announcement for a new Python web framework. He called it Flask, and it claimed to be a microframework that fit in a single file. The entire thing was presented as a joke. A parody of the heavyweight frameworks that dominated Python web development, Django with its batteries-included philosophy and its insistence on doing things the Django way.
But people tried it. And it worked. Because underneath the joke, Flask was simply Werkzeug and Jinja2 glued together with some session handling and a routing layer. The components were real. The architecture was sound. The joke was just the packaging. When developers started asking for a real version, Ronacher obliged. Flask became Flask, and within a few years it was one of the two most popular web frameworks in Python, used by forty-two percent of Python developers according to survey data.
Flask is based on good intentions. I wanted something simple that gets out of your way, and lets you build what you actually need to build.
Here is what makes this remarkable. Flask is not really a framework in the traditional sense. It is a thin layer over two libraries that Ronacher had already written. Werkzeug handles the HTTP plumbing. Jinja2 handles the page rendering. Click, another Ronacher creation, handles command-line interfaces. itsdangerous, another Ronacher creation, handles cryptographic signing for session cookies. MarkupSafe, another Ronacher creation, handles the HTML escaping that keeps Jinja2 templates secure.
One person wrote the web server interface, the template engine, the command-line toolkit, the session security, and the HTML safety layer, and then wrapped them all in a framework that became the foundation for thousands of applications. The bus factor was not low. The bus factor was one.
For years, all of these libraries lived under the Pocoo umbrella, the informal team name left over from the bulletin board that never happened. But Pocoo was not a real organization. It was a name on a website and a shared IRC channel. As the projects grew, as Flask became critical infrastructure for companies that employed hundreds of engineers, the gap between the importance of the software and the formality of its stewardship became uncomfortable.
In two thousand sixteen, Ronacher formalized things under a new name. The Pallets Projects. A proper GitHub organization, a proper website, a proper governance structure. Flask, Jinja2, Werkzeug, Click, itsdangerous, and MarkupSafe all moved under the Pallets banner. The name was understated. Pallets. Wooden platforms that hold things up. Infrastructure you do not notice until it is gone.
But even with the organizational upgrade, Ronacher remained the gravitational center. The Python Software Foundation recognized this with a Fellow designation in two thousand twelve and a Community Service Award in two thousand fourteen. The Shuttleworth Foundation gave him a flash grant the same year. These were acknowledgments that one person's side projects had become load-bearing walls in the Python ecosystem.
Jinja2 was built for web development. It was designed to generate HTML pages. But template engines turn out to be useful for generating any kind of structured text, and once people realized this, Jinja2 escaped the web entirely.
Ansible discovered it first. When you write an Ansible playbook to configure a hundred servers, you need the configuration files to be slightly different on each machine, a different IP address here, a different hostname there, a different database password on the production server versus the staging server. Jinja2 was the perfect tool. You write a template of your nginx configuration with placeholders, and Ansible fills in the values for each machine. Every Ansible playbook, every Ansible role, every Ansible template file uses Jinja2 syntax. If you have ever managed infrastructure with Ansible, you have written Jinja2 whether you knew it or not.
SaltStack did the same thing. Then dbt, the data build tool that transformed how analytics engineers work with SQL, adopted Jinja2 for its query templating. You write SQL with Jinja2 loops and conditionals, and dbt renders it into the actual queries that run against your data warehouse. Apache Airflow uses Jinja2 to template its workflow definitions. Cookiecutter uses it to generate project scaffolding. MkDocs uses it for documentation themes. Sphinx, which Georg Brandl co-created with Ronacher back in the Pocoo days, uses Jinja2 for its own templates.
The pattern is always the same. Someone needs to generate structured text where most of the content is fixed but some parts vary. They look for a template engine. They find Jinja2. It has been on PyPI since two thousand eight. It has eleven billion total downloads. It gets roughly eighty-five million downloads per week. And because it is a dependency of Flask, which is a dependency of thousands of applications, and a dependency of Ansible, which is installed on millions of infrastructure machines, Jinja2 is one of the most widely deployed Python packages in existence.
Most open source stories in this series follow a pattern. Someone builds one thing. That thing becomes important. The weight of maintaining it defines their life. Ronacher broke this pattern by building not one important thing but an entire interlocking ecosystem, and then going to work somewhere else.
In two thousand fourteen, he joined Sentry, the error tracking and performance monitoring company. He stayed for a decade. Not as a figurehead or an advisor, but as an engineer and eventually Vice President of Platform, leading their Vienna office as it grew to more than fifty employees. During those ten years, he continued maintaining the Pallets projects, but he also started writing Rust. He built redis-rs, a lean Redis driver for Rust. Then insta, a snapshot testing tool that brought the same philosophy of clean, obvious interfaces to test output. Then similar, a diffing library. And then MiniJinja, a stripped-down reimplementation of his own template engine, rebuilt in Rust for a new era. Not for web pages this time, but for prompts fed to large language models. The same idea, the same obsession with developer experience, reborn in a language designed for the performance constraints that Python could never solve.
Think about that for a moment. The template engine he built for generating web pages in two thousand eight is now being reimplemented in Rust for generating prompts for artificial intelligence systems in two thousand twenty-five. The abstraction was so clean, the idea so fundamental, that it outlived its original context by nearly two decades and found a second life in a technology that did not exist when Jinja2 was written.
And then there is Rye. In two thousand twenty-three, Ronacher built an experimental Python package manager. It was opinionated, fast, and different from pip in ways that made some people uncomfortable and other people excited. He ran it as a personal project for a while, iterating on the design, gathering feedback. And then, in two thousand twenty-four, he donated it to Astral, the company founded by Charlie Marsh. If you remember episode ten, Marsh is the Princeton graduate who built Ruff, the Rust-based Python linter, and then uv, the Rust-based pip replacement that is eight to ten times faster than pip. Ronacher looked at what Astral was building and decided his experimental package manager would be better off in their hands. So he gave it away.
I have always believed that the best tools should be liberally licensed, which means you can do with them whatever you want.
In early two thousand twenty-five, Ronacher left Sentry and co-founded Earendil, a public benefit corporation focused on human-centered software and open protocols. He was thirty-five. He had been writing consequential open source software for more than half his life.
Let us look at what Jinja2 actually pulls in. The answer is almost nothing. Jinja2 has one hard dependency. MarkupSafe. Which Ronacher also wrote. MarkupSafe provides fast HTML escaping, the security mechanism that prevents cross-site scripting attacks in templates. It is a tiny library, mostly C code for performance, and it exists because escaping HTML correctly is both critical and surprisingly tricky to get right.
That is the entire tree. Jinja2 depends on MarkupSafe. MarkupSafe depends on nothing. Two packages, both written by the same person, both maintained under the same organization. Compare this to the dependency trees we have seen in earlier episodes. requests pulls in urllib3, certifi, charset-normalizer, and idna. Express pulls in thirty direct dependencies. A fresh React Native project installs over one hundred twenty thousand files. Jinja2 installs two packages. The template engine and its HTML escaper. That is it.
This is not an accident. It reflects a design philosophy that Ronacher has articulated throughout his career. Build small, well-defined components. Make each one do one thing. Give each one a clear API. Then compose them. Flask is Werkzeug plus Jinja2 plus Click plus itsdangerous plus MarkupSafe. Five packages, each independent, each useful on its own, composed into a framework. The Pallets ecosystem is a case study in the Unix philosophy applied to Python web development.
Most of the stories in this series have a crisis. A maintainer burns out. A company changes the license. A fork splits the community. A vulnerability goes undetected for years. Jinja2 has none of these. It has had the same primary author since two thousand eight. It has never changed its license. It has never been forked in anger. It has never had a security incident that made the news.
This is partly because Ronacher did not disappear. He did not burn out. He did not hand the project to a stranger. He went to work at Sentry, earned a salary doing something other than maintaining his open source projects, and continued to steward them on the side. The Pallets organization gave the projects a bus factor greater than one, even though Ronacher remained the primary contributor. When he stepped away from day-to-day maintenance, there were other people who could keep things running.
But it is also because template engines, once they work, do not need to change very much. The core problem, generating structured text from data and a template, was solved by two thousand eight. The improvements since then have been incremental. Better performance. Better error messages. Python three support. Async support. Native support for newer Python features. Jinja2 is on version three point one now, and the version number reflects the reality. This is mature, stable software. It does not need a revolution every two years. It needs someone to fix the occasional bug and make sure it works with the latest Python release. Ronacher and the Pallets team have done exactly that for seventeen years.
If you have ever built a Flask or FastAPI project that renders a web page, Jinja2 was in your requirements file. Event management systems, dashboards, reminder services, content management tools, anything that serves HTML to a browser through a Python backend is almost certainly running Jinja2 templates. But that is only the explicit dependency. Every FastAPI project has Jinja2 available because it ships as an optional integration, and most projects use it for at least some HTML rendering.
And then there is the invisible layer. If your servers are managed by Ansible, every nginx configuration, every systemd service file, every deployment script that gets templated and pushed to production uses Jinja2 syntax. The curly braces and percent signs are in the infrastructure, not just the applications.
Armin Ronacher was fourteen when he won his first programming award. He was nineteen when he released the template engine you are listening through right now. He was twenty when he turned an April Fools' joke into one of Python's most popular frameworks. He was thirty-four when he gave away a package manager to a company that could build it better. And somewhere in Graz, in two thousand two, a thirteen-year-old was writing QBasic games with no idea that a pun about Japanese temples would end up running on half the servers on the internet. Temple. Template. Jinja. The pun still works. The code works better.
Open a terminal and type pip install jinja2. Then open a Python shell. Type from jinja2 import Template. Then type Template open parenthesis quote Hello double curly brace name double curly brace close quote close parenthesis dot render open parenthesis name equals quote world quote close parenthesis. You will see Hello world. That is the entire mental model. A string with holes in it, filled at runtime. One line to understand why every web framework, every infrastructure tool, every configuration system reaches for Jinja when it needs templates.
That was episode fifteen.