Open-source project
triton-inference-server/python_backend avatar
triton-inference-server/python_backend

Triton Python backend: serving Python models without writing C++

Triton backend that enables pre-process, post-processing and other logic to be implemented in Python.

682 stars207 forksC++BSD-3-Clause

At a glance

What is it?
An NVIDIA maintained backend for Triton Inference Server that lets you implement a model, its pre-processing, its post-processing and any business logic in Python, with a versioned model lifecycle and a set of execution modes for streaming and stateful work. It is the piece that makes Python a first class citizen in a Triton deployment.
Who is it for?
Adopt the Triton Python backend if you already run Triton Inference Server and want your model logic in Python, since the pre-processing and post-processing that would otherwise force you into C++ stays in the language your team already uses. Skip it if you serve a single small model outside Triton, because the backend brings its own Python runtime and version constraints with it.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the backend removes from a Triton deployment

The goal of the Python backend is to let you serve models written in Python by Triton Inference Server without having to write any C++ code. That sentence is the whole pitch, and the reason it matters is that a typical Triton model is not just the network.

Triton's model contract is wider than a forward pass. A model in Triton usually has pre-processing to turn the request into tensors, the inference itself, and post-processing to turn the output back into a response. The project description of this repository says it enables pre-process, post-processing and other logic to be implemented in Python.

So the alternative, before this backend existed, was to write the surrounding logic in C++ even when the model was written in Python, which means a C++ extension, a binding layer and two languages to reason about for what is often a straightforward transformation.

The code is C++ even though the interface is Python, and the licence is BSD-3-Clause with a copyright line running from 2020 to 2026 for NVIDIA CORPORATION and its affiliates. The repository tree reinforces the structure: src holds the backend itself, examples holds a directory per feature, cmake holds the build glue, inferentia holds the AWS Inferentia specific work, and there is a devcontainer and a .vscode directory for development.

The examples directory is the fastest way to understand the contract, because there is one directory per capability: add_sub, auto_complete, bls, bls_decoupled, custom_metrics, decoupled, instance_kind, jax, preprocessing, pytorch. That list is effectively the feature surface of the backend, and the documentation table of contents mirrors it exactly.

The quick start, and the tag discipline it depends on

The quick start is seven steps, and the reason it is longer than a typical getting-started is that Triton itself is a server, a container and a model repository, and the Python backend has to be built to match.

Step one is running the Triton Inference Server container:

bash
docker run --shm-size=1g --ulimit memlock=-1 -p 8000:8000 -p 8001:8001 -p 8002:8002 --ulimit stack=67108864 -ti nvcr.io/nvidia/tritonserver:<xx.yy>-py3

You replace the version placeholder with the Triton version, and the example given is 21.05. Note the three published ports, 8000, 8001 and 8002, which are the HTTP, gRPC and metrics interfaces of the server, and the two ulimit settings, memlock unlocked and a 64 megabyte stack. Both matter for a Python backend specifically, because the shared memory and the lock behaviour are what the backend uses to pass tensors, and the shared memory size is why the container is started with a one gigabyte shared memory segment.

Step two clones the backend repository, and the branch matters:

bash
git clone https://github.com/triton-inference-server/python_backend -b r<xx.yy>

The branch is the release branch matching the Triton version, r21.06 is the example given elsewhere in the document. This is the single most common source of confusion for newcomers: the backend is not a standalone package you pip install, it is a C++ shared library built against a specific Triton release, so the branch, the container tag and the server version all have to line up.

Steps three through five install an example model and start the server:

bash
cd python_backend
mkdir -p models/add_sub/1/
cp examples/add_sub/model.py models/add_sub/1/model.py
cp examples/add_sub/config.pbtxt models/add_sub/config.pbtxt

The model directory layout is visible in those commands, a models directory containing a version directory named 1 with a model.py and a config.pbtxt in it, and the server is then started against the current directory:

bash
tritonserver --model-repository `pwd`/models

Steps six and seven repeat the container and clone dance for a client, and run the example client script. The repository is cloned into the client container too, which tells you the client needs the example files, not the backend itself.

The model lifecycle, and what the model has to implement

A model written for this backend implements a small set of methods, and the documentation table of contents lists them: initialize, execute, finalize and is_ready, plus auto_complete_config.

initialize is where the model is set up, and it is the point at which a model that loads a framework such as PyTorch or TensorFlow does its loading. execute is the request handling method, and it has several documented variants: a default mode, a decoupled mode, an async execute variant, request cancellation handling and request rescheduling. finalize is the shutdown hook, and is_ready is the readiness check Triton uses before routing traffic to the instance.

auto_complete_config is the interesting one for anyone who has written a Triton model in another language, because it lets the backend generate the model configuration rather than requiring you to hand-write config.pbtxt. The auto_complete example directory in the tree exists to exercise it.

There is also a model instance kind feature, with its own example directory, and a section on inference request parameters and inference response parameters, which are the names Triton uses for the extra fields you can attach to a request and a response beyond the tensors.

The two framework sections are worth a note. The documentation has a PyTorch section and a TensorFlow section, each followed by a determinism subsection. Determinism is a real concern here, because the same input should produce the same output for a model under test or under a regression check, and both frameworks have settings that affect it. The fact that the documentation calls this out per framework suggests it is a known source of surprise rather than a theoretical one.

Decoupled mode, stateful models and the limit of business logic scripting

Triton's execution modes come in several shapes, and the backend supports the ones that a Python implementation can reasonably support.

The default mode is the straightforward one, one request in, one response out. Decoupled mode is for streaming: the model sends responses as it produces them rather than waiting for the whole sequence, and there is an async execute variant for that. Business logic scripting, or BLS, is the mechanism for one model to call another, and the documentation covers BLS with decoupled models, a model loading API, and BLS with stateful models.

Stateful models are the other axis, where the model keeps state across requests in the way a sequence or state machine model needs.

And then there is a section simply titled Limitation, in the business logic scripting part of the table of contents. The documentation does not spell it out in the excerpt available here, so the honest statement is that the project documents a specific limitation in this area and you should read that section before designing around BLS.

There is a decoupled example, a bls example, a bls_decoupled example and a preprocessing example in the tree, and the examples list in the documentation covers the same ground: AddSub in NumPy, AddSubNet in PyTorch, AddSub in JAX, Business Logic Scripting, Preprocessing, Decoupled Models, Model Instance Kind, Auto-complete config and Custom Metrics. If you want to know what the backend does, that list of ten examples is the most complete map the project offers, and each one is a small, readable program rather than a framework.

Building from source means aligning three repository tags

The build section is where the coupling with the rest of Triton becomes explicit, and it is the part you will hit if the prebuilt container is not what you need.

The requirements are cmake 3.17 or later, numpy, rapidjson-dev, libarchive-dev and zlib1g-dev. On Debian or Ubuntu:

bash
sudo apt-get install rapidjson-dev libarchive-dev zlib1g-dev

and numpy is installed with:

bash
pip3 install numpy

Then the build itself, with three CMake variables that name the branch of three separate Triton repositories:

bash
mkdir build
cd build
cmake -DTRITON_ENABLE_GPU=ON -DTRITON_BACKEND_REPO_TAG=<GIT_BRANCH_NAME> -DTRITON_COMMON_REPO_TAG=<GIT_BRANCH_NAME> -DTRITON_CORE_REPO_TAG=<GIT_BRANCH_NAME> -DCMAKE_INSTALL_PREFIX:PATH=`pwd`/install ..
make install

You replace the placeholder with the GitHub branch that you want to compile, and for release branches it should be r21.06 as the example says. The three repositories pulled in are the backend, the common library and the core server, and the documentation is explicit that the branch should be the same as the Python backend repository branch that you are trying to compile.

If the CMake variables are not specified, the main branch of those repositories is used. That default is worth reading carefully. Building against main of three repositories means you get whatever is current rather than a coherent release, and for a component that has to be ABI compatible with a specific server release, main is the wrong default in production.

The TRITON_ENABLE_GPU flag is the other decision in that line, and it pairs with the interoperability story: the GPU path exists, and the way tensors move between the backend and your framework is described with the dlpack functions in the next section.

Tensors cross the boundary as PyCapsules, and device placement is your problem

The interoperability section describes how a Python backend and a Python framework exchange tensors, and the mechanism is PyCapsule rather than a copy.

The documented functions are pb_utils.Tensor.to_dlpack() returning a PyCapsule, pb_utils.Tensor.from_dlpack() returning a Tensor, and pb_utils.Tensor.is_cpu() returning a bool. There is also a section on input tensor device placement.

The reason dlpack is the right mechanism is that it allows a tensor to be passed without serialising it, so a zero-copy handoff between the framework and the backend is possible when the device layout allows it. The cost is that the contract is explicit about devices, and is_cpu() exists because you need to know where the data is before you can hand it to a framework that expects it somewhere particular.

This is the part of the backend where the abstraction leaks. Your model code is Python and looks like Python, but the data has a device, and the device has to match what the framework wants. A model that works on CPU can fail on GPU, or the other way around, and the failure shows up as an error rather than a wrong answer, which is the better of the two outcomes but still a failure.

The framework support is listed as PyTorch, TensorFlow, JAX among the examples, and the tree has a jax example directory and a pytorch example directory. The JAX example is worth noting because JAX is the framework where device placement is most explicit, and the presence of an example for it suggests the backend is used with more than one framework in practice.

Where the Python backend is the wrong choice

There are clear cases where this component is the wrong answer, and they are mostly about the cost it carries.

The first is if you are not running Triton at all. A single model served by a Python process with a FastAPI wrapper is simpler, easier to debug and has none of the version alignment burden. The backend exists to make Python work inside a server whose model contract was designed around compiled languages.

The second is the version coupling. The branch you clone, the container tag you run, the three repository tags you pass to CMake and the Python version inside the image all have to be coherent. A team that upgrades Triton periodically is signing up for that coordination every time, and the documentation's own insistence that the branch should match is the tell.

The third is that a Python backend has a different performance profile from a compiled one, and the documentation does not make grand claims about it. The point of Triton is predictable, batched, low overhead serving. Adding an interpreted language into the request path moves you in the opposite direction from that goal, and the right place for Python is often the pre-processing and post-processing rather than the hot path.

The fourth is the undocumented surface. The lifecycle contract is four methods, which is small, but the modes, the configuration options, the business logic scripting limitation and the per-framework determinism notes are all spread across a long document, and the readme itself says the documentation site carries the full guides and the complete API. Expect to read the site rather than the repository.

And the fifth is the AWS Inferentia path, which has its own directory in the tree and a section in the documentation. If you deploy on Inferentia, that is a separate surface to evaluate, and the readme does not describe how it differs.

What the state of the project means for a dependency

The maintenance picture for this repository is unusual and worth stating precisely rather than in general terms.

The repository is not archived, and the last push was on 2026-10-15. There are no GitHub releases, which is consistent with how Triton backends are distributed: the backend ships inside the Triton container, so the container tag is the release unit rather than a tag in this repository.

That distribution model has a consequence for how you upgrade. There is no backend version to pin independently, because what you get is the backend compiled into a particular Triton server image. Your upgrade unit is the server image, and the branch you build from, r21.06 style, is the thing that tells you which server it matches.

The copyright header says 2020 to 2026, which is the honest indication of the project's age, and the code is C++ with a Python interface, so a contributor is working in two languages and against the Triton core APIs.

The licence is BSD-3-Clause, and the header in the repository is the full three clause BSD text with the NVIDIA copyright, which is a permissive licence with no copyleft obligation. For a component that NVIDIA maintains as part of a larger inference stack, that is the expected licensing choice, and it means you can patch it and ship it without the obligations that a GPL component would bring.

The development workflow is documented in the tree rather than the readme. There is a .devcontainer directory, a .vscode directory, a .clang-format file for the C++ side, a .pre-commit-config.yaml, and a pyproject.toml that exists mainly to configure codespell for the pre-commit hook. There is also a section on running with Inferentia, one on logging and one on development with VSCode, and the reporting problems section points to a discussion route on the project tracker rather than only issues.

Editorial conclusion

Adopt the Triton Python backend if you already run Triton Inference Server and want your model logic in Python, since the pre-processing and post-processing that would otherwise force you into C++ stays in the language your team already uses. Skip it if you serve a single small model outside Triton, because the backend brings its own Python runtime and version constraints with it. Verify first that the Python version inside the container matches what your model needs, that the Triton release tag and the backend branch tag you build are the same, and that your model satisfies the lifecycle the backend expects before you rely on it in production.

Frequently asked questions

What does the Triton Python backend let me do?

It lets you serve models written in Python by Triton Inference Server without having to write any C++ code, and it enables pre-process, post-processing and other logic to be implemented in Python. A model implements initialize, execute, finalize and is_ready, plus auto_complete_config.

How do I run a Triton server with the Python backend?

Run the Triton container with docker run --shm-size=1g --ulimit memlock=-1 -p 8000:8000 -p 8001:8001 -p 8002:8002 --ulimit stack=67108864 -ti nvcr.io/nvidia/tritonserver:<xx.yy>-py3, clone the backend on the matching r<xx.yy> branch, copy an example model into a models directory, and start tritonserver --model-repository `pwd`/models.

What do I need to build the Triton Python backend from source?

cmake 3.17 or later, numpy, rapidjson-dev, libarchive-dev and zlib1g-dev. You then run cmake with TRITON_ENABLE_GPU=ON and matching TRITON_BACKEND_REPO_TAG, TRITON_COMMON_REPO_TAG and TRITON_CORE_REPO_TAG values, followed by make install.

Which execution modes does the Triton Python backend support?

The documentation covers a default mode, decoupled mode with an async execute variant, request cancellation handling, request rescheduling, and business logic scripting including BLS with decoupled models, a model loading API and BLS with stateful models. There is also a documented limitation in the business logic scripting section.

How do tensors move between the Python backend and a framework?

Through dlpack. pb_utils.Tensor.to_dlpack() returns a PyCapsule, pb_utils.Tensor.from_dlpack() returns a Tensor, and pb_utils.Tensor.is_cpu() tells you where the data lives, which matters because input tensor device placement is your responsibility.

How is the Triton Python backend licensed and how current is it?

It is BSD-3-Clause, with the full NVIDIA copyright header running from 2020 to 2026. The repository is not archived and the last push was on 2026-10-15, and there are no GitHub releases because the backend ships inside the Triton server container rather than as a separate package.

Official sources

  1. Issues
  2. License: BSD-3-Clause
  3. README
  4. triton-inference-server/python_backend on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/triton-inference-server-python-backend.svg)](https://hysenlabs.com/projects/triton-inference-server-python-backend)

Was this useful?