# nano_build_opencv compiles OpenCV from source on a Jetson without guessing your memory

> nano_build_opencv is a shell script that builds OpenCV and the opencv_contrib modules from source with Python 3 bindings, written for Tegra but working on any Debian or Ubuntu machine. The detail that matters on a Nano is the job count, which is derived from CPU count and available RAM plus swap, so the build does not run the board out of memory halfway through.

**mdegans/nano_build_opencv** — Build OpenCV on Nvidia Jetson Nano

- Repository: https://github.com/mdegans/nano_build_opencv
- Stars: 551 · Forks: 204
- Language: Shell
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/mdegans-nano-build-opencv

## One script, written for Tegra but not limited to it

The repository is a single shell script with a README, and the script is the product.

The description says what it does: it builds OpenCV and the opencv_contrib modules from source, with Python 3 bindings. The framing note is that it was written for Tegra, covering the Jetson Nano, TX1, TX2, Xavier and Orin, but works on any Debian or Ubuntu machine, on x86_64 or aarch64, with or without CUDA. It is tested in CI on Ubuntu 18.04, 20.04, 22.04 and 24.04, on both x86_64 and arm64.

That combination is the interesting thing about the script. The premise is that a Jetson user has a specific CUDA version tied to their JetPack release and needs OpenCV compiled against it, since the prebuilt packages in the NVIDIA ecosystem are tied to particular versions and the distribution packages do not know anything about the hardware. But the script is written to be portable, because the build logic that matters, the CMake configuration and the source fetch, is the same everywhere.

The scope note about opencv_contrib is worth reading carefully. The option that builds the contrib modules is on by default and the description says it includes the nonfree ones, which is the SIFT and SURF family of algorithms whose patent situation made them awkward to ship in the main distribution for years. If you build the contrib modules, you get them, and the licensing terms are the ones those algorithms carry.

There is also a link to a related thread on the Nvidia developer forum, which is the usual place where a script like this gets its first round of real-world problems.

The project is MIT licensed, Shell, with the default branch master and the last push on 2026-10-08. The repository contains only a .github directory, a .gitignore, the LICENSE, the README and build_opencv.sh.

## Running it and picking a version

The usage section is three commands, and the middle one is the interesting one.

```shell
./build_opencv.sh
```

That builds the default version. To choose a version, you pass an OpenCV git tag or branch as an argument:

```shell
./build_opencv.sh 4.14.0
```

And the default is 4.14.0. The caveat attached to it is that very old versions have not been tested to build and may require script modifications, which is an honest statement about the difference between a version the script has been exercised against and one it has not.

So the version is a git reference rather than a tarball URL, which means the script fetches from the OpenCV repository and checks out what you asked for. The practical consequence is that you can point it at a branch, which is useful when a fix is on its way to a release and you want to test it before it lands.

There is a related constraint that comes from the hardware rather than the script. The CUDA version shipped with your JetPack limits which OpenCV versions will build, and for JetPack 4.4 the minimum is 4.4.0.

That is the constraint worth understanding before you start, because it is not obvious. Newer OpenCV releases require a newer CUDA toolkit than older JetPack releases ship, so a board on JetPack 4.4 cannot build a recent OpenCV at all. The script does not paper over that; it is a hard constraint from the toolkit you have.

The dependency install is on by default through the INSTALL_DEPS option, which installs build dependencies with apt-get. On a Jetson that matters because the NVIDIA packages are not in the usual Ubuntu repositories and the script leaves those alone, installing only the ordinary build dependencies.

## The test mode is the part that makes the script trustworthy

There is a third argument to the script, and it is the one that separates a build script from a package manager.

```shell
./build_opencv.sh 4.14.0 test
```

Running the tests also fetches the test data from opencv_extra and runs the accuracy tests before installing. The script exits non-zero if any test fails, and TEST_EXCLUDE can skip known upstream failures.

That ordering is the important design. The tests run before the install, not after, so a build that compiles but computes the wrong answers is caught before it is installed over your working copy. A build script that installs first and hopes is a common failure mode, and this one refuses it.

The accuracy tests are the right choice for this purpose. OpenCV's own test suite is largely a correctness suite rather than a unit suite: it checks that a filter produces the expected output against reference data, which is exactly what you want when the risk is that a compiler flag, an instruction set or a CUDA kernel has quietly changed the numerical results.

The opencv_extra repository is the standard companion that holds the test data and the test modules, so pulling it is a necessary part of the process rather than an extra.

The TEST_EXCLUDE option is a gtest filter, and the example given is a pattern of the form Media.audio/*:Highgui_GUI.*. That tells you the exclusion is expressed in the test framework's own selector syntax rather than in a bespoke list, so you can exclude anything the framework can select. The reason it exists is known upstream failures, which in a project with this many platforms and configurations is not a rare situation.

When there is no display, the tests run under Xvfb, which is the standard way to give a graphical test suite a virtual framebuffer on a headless machine. For a Jetson accessed over SSH with no monitor attached, that is what makes the test mode usable at all.

The CLEANUP option controls whether the build directory is removed, with values yes, no or ask, and it defaults to no when not run interactively. That default is a good one, since a non-interactive run is usually a CI job and you want the logs and artefacts left behind.

## The options table is where the portability shows

The options are set as environment variables, so the whole configuration is a prefix on the command line, and the table is the real specification of the script.

PREFIX sets the install prefix and defaults to /usr/local, with the note that sudo is only used if it is not writable. That single behaviour is what lets the same script work for a system-wide install and for a personal install into a local prefix.

BUILD_DIR sets where sources are fetched and built, defaulting to /tmp/build_opencv. Keeping it out of the source tree matters on a device with limited storage.

JOBS sets the parallel build jobs. The default is auto, and the description is the interesting one: by default it is based on CPU count and RAM plus swap, so a Nano will not run out of memory. That is the single most useful thing this script does for a Jetson, where the core count is high relative to the memory and a naive make -j would drive the board into swap or an out-of-memory kill partway through a two-hour compile.

WITH_CUDA takes ON, OFF or auto, and auto is the default, meaning on if nvcc is found, including in /usr/local/cuda/bin. That path is included deliberately because on a Jetson the CUDA toolkit is installed outside the default PATH.

CUDA_ARCH_BIN sets the compute capabilities, with an example value of 8.7, and it is detected from the Jetson SoC or nvidia-smi, otherwise OpenCV detects the local GPU.

WITH_CONTRIB is ON by default and builds the opencv_contrib modules including the nonfree ones. INSTALL_DEPS is ON by default and installs build dependencies with apt-get. PYTHON3 selects the interpreter to build the bindings for, with the important note that it needs numpy and that you should use /usr/bin/python3 if pyenv or conda comes first on your PATH. That is a real and common failure, since the build will pick up a different Python than the one you expect and the resulting bindings will not import.

EXTRA_CMAKE_FLAGS passes extra flags to cmake, with an example of enabling Qt. And CLEANUP and TEST_EXCLUDE, discussed already, cover the build directory and the test selection.

cuDNN and the CUDA DNN backend are enabled automatically when their headers are found, which is the right default because it is what you want on a device where the headers are present and you did not intend to leave the backend off.

## Jetson specifics, and what CI cannot check

The Jetson section is the one with the hardware-specific knowledge, and it is worth reading in full.

On a Jetson the CUDA architecture is picked from the SoC: Nano and TX1 are 5.3, TX2 is 6.2, Xavier is 7.2 and Orin is 8.7. CUDA is found in /usr/local/cuda even if it is not on your PATH.

That table is the piece of information the script encodes that you would otherwise have to look up for your board. The compute capability determines which GPU code is generated, and getting it wrong means either failing to run on the GPU or generating code for a different architecture.

The second capability is building without a GPU driver, for example in a container, by linking against the CUDA stub library. In that case you must set CUDA_ARCH_BIN, because otherwise OpenCV builds for every architecture, which on a machine with a lot of disk and no GPU produces a very large and slow build.

Then the CI limits are stated with unusual clarity. CI builds the script on Ubuntu 18.04 with JetPack 4 and Ubuntu 20.04 with JetPack 5 on arm64, and does a CUDA 12.8 with cuDNN 9 build for Orin with sm_87. It has no Jetson hardware or GPU, so nothing CUDA related is run in CI.

That last sentence is the one to keep in mind. The CI proves the script configures and compiles correctly across the matrix, and the accuracy tests do run under Xvfb on the CPU path, but a CUDA kernel producing wrong results on real silicon would not be caught by this repository's own checks. For a Jetson-specific project, that is a reasonable and honest trade, and it is the sort of thing to keep in mind when reading a green build.

The script also notes that on non-Debian distributions the dependencies are not installed automatically: install them with your package manager, run with INSTALL_DEPS=OFF, and the rest of the script will work.

The repository itself is minimal, with the workflow directory, the ignore file, the licence, the README and the one script. Everything else lives in that file.

## Conclusion

Adopt nano_build_opencv if you need an OpenCV build matched to your CUDA version and your hardware rather than whatever a package manager offers, since it handles the Jetson specifics and the memory limits for you. Skip it if your distribution ships a usable OpenCV package and you do not need particular modules, and if you need CUDA features that only work on real hardware, since the CI never runs anything CUDA related. Verify first which OpenCV version your JetPack's CUDA version permits, that your Python interpreter has numpy and is the system one if you use pyenv or conda, and what you want to keep, since the build directory is a configurable location that is removed after installing.

## FAQ

### What does nano_build_opencv do and where does it run?

It is a bash script that builds OpenCV and the opencv_contrib modules from source with Python 3 bindings. It was written for Tegra, covering the Jetson Nano, TX1, TX2, Xavier and Orin, but works on any Debian or Ubuntu machine, on x86_64 or aarch64, with or without CUDA, and is tested in CI on Ubuntu 18.04, 20.04, 22.04 and 24.04 on both architectures.

### How do I build OpenCV and run the tests?

Run `./build_opencv.sh` for the default version, which is 4.14.0, or pass an OpenCV git tag or branch such as `./build_opencv.sh 4.14.0`. To run the tests, pass test as a third argument: `./build_opencv.sh 4.14.0 test`. That also fetches the test data from opencv_extra and runs the accuracy tests before installing, under Xvfb when there is no display, and the script exits non-zero if any test fails unless it is excluded with TEST_EXCLUDE.

### What are the most important environment variables?

PREFIX sets the install prefix, defaulting to /usr/local with sudo used only when it is not writable, and BUILD_DIR sets where sources are fetched and built, defaulting to /tmp/build_opencv. JOBS controls parallel build jobs and defaults to a count derived from CPU count and RAM plus swap so a Nano does not run out of memory. WITH_CUDA is ON, OFF or auto and is on when nvcc is found, CUDA_ARCH_BIN sets the compute capability detected from the SoC or nvidia-smi, WITH_CONTRIB and INSTALL_DEPS are on by default, and PYTHON3 picks the interpreter and needs numpy.

## Sources

- [Issues](https://github.com/mdegans/nano_build_opencv/issues)
- [License: MIT](https://github.com/mdegans/nano_build_opencv/blob/master/LICENSE)
- [mdegans/nano_build_opencv on GitHub](https://github.com/mdegans/nano_build_opencv)
- [README](https://github.com/mdegans/nano_build_opencv/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mdegans-nano-build-opencv
