# vk-bootstrap: turning 500 lines of Vulkan setup into about 50

> vk-bootstrap is a C++17 utility library that handles Vulkan instance creation, physical device selection, device creation, queue retrieval and swapchain setup, with a builder API and result objects that carry errors. It is not header only, and on Unix it resolves Vulkan symbols at runtime through the dynamic linker.

**charles-lunarg/vk-bootstrap** — Vulkan Bootstrapping Iibrary

- Repository: https://github.com/charles-lunarg/vk-bootstrap
- Stars: 1,297 · Forks: 118
- Language: C++
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/charles-lunarg-vk-bootstrap

## The five chores this library takes off your plate

vk-bootstrap exists to remove the tedious opening act of a Vulkan program. The list it names is specific: instance creation, physical device selection, device creation, getting queues, and swapchain creation.

That list is worth reading closely, because it is exactly the part of Vulkan that every application repeats and nobody enjoys. The raw API asks you to enumerate physical devices, query their properties and features, decide which one to use, enumerate its queue families, pick indices, create a logical device with the right extensions enabled, and then build a swapchain against a surface. The example in the project shows the contrast in a single line: basic_usage.cpp takes 500 lines of raw Vulkan code and is slimmed down to about 50.

On top of the core chores the library adds conveniences that are easy to underestimate. Validation layers can be requested during instance build, a debug messenger can be set up in the same call, extensions can be enabled on a physical device, and a GPU can be chosen by criteria such as features, extensions and memory rather than by index.

The primary language is C++, the licence is MIT, and the repository is not archived with the last push on 2026-10-07, so the library is being worked on.

## Builder objects and result objects instead of raw Vulkan calls

The API shape is what makes the library pleasant to use. Each stage is a builder that returns a result, and the result carries either the object or the reason it failed:

```cpp
vkb::InstanceBuilder builder;
auto inst_ret = builder.set_app_name ("Example Vulkan Application")
                    .request_validation_layers ()
                    .use_default_debug_messenger ()
                    .build ();
if (!inst_ret) { /* report */ }
vkb::Instance vkb_inst = inst_ret.value ();
```

The same pattern repeats for the physical device, where a selector takes the instance and a set of requirements, and for the device, where a device builder takes the selected physical device:

```cpp
vkb::PhysicalDeviceSelector selector{ vkb_inst };
auto phys_ret = selector.set_surface (surface)
                    .set_minimum_version (1, 1)
                    .require_dedicated_transfer_queue ()
                    .select ();
if (!phys_ret) { /* report */ }

vkb::DeviceBuilder device_builder{ phys_ret.value () };
auto dev_ret = device_builder.build ();
if (!dev_ret) { /* report */ }
vkb::Device vkb_device = dev_ret.value ();
```

Queue retrieval follows the same convention, so a missing queue family is reported the same way as a failed instance creation:

```cpp
auto graphics_queue_ret = vkb_device.get_queue (vkb::QueueType::graphics);
if (!graphics_queue_ret)  { /* report */ }
VkQueue graphics_queue = graphics_queue_ret.value ();
```

Two details are easy to miss. The calls are written with a space before the parenthesis, and the result type is checked with a plain boolean test, which suggests an expected-like result object rather than a thrown exception. A library that chose exceptions would be a different style choice, and this one keeps errors in the return value.

## Picking a GPU by requirement instead of by enumeration order

The physical device selector is where the library earns its keep, because a hardcoded device index is exactly what breaks when a user has a different machine.

The selector API is criteria-based. You set a surface, you declare a minimum version with set_minimum_version, and you can require specific capabilities, in this example a dedicated transfer queue through require_dedicated_transfer_queue. The library then selects a device that satisfies those conditions.

The optional-configuration list in the project goes further: it allows selecting a GPU based on a set of criteria like features, extensions and memory. In other words, a program can state what it needs in terms of features, extensions and memory, and let the library find the best candidate that satisfies the constraints rather than trusting enumeration order.

The example directory shows the shape of that in practice, with a basic_usage.cpp, a triangle.cpp that renders a triangle to the screen, a simple_compute.cpp that performs a simple add with a compute shader and no surface in fewer than 300 lines, a custom_debug_callback.cpp, and a system_info.cpp. The compute example is the interesting one for evaluation, because it drops the swapchain and surface from the picture entirely, which is what a compute-only tool needs and what the raw API makes awkward to isolate.

## Vendoring three files, or wiring it into CMake

Integration is offered in three shapes, and the choice matters for how you maintain the dependency.

The copy-paste route takes three files, src/VkBootstrap.h, src/VkBootstrapDispatch.h and src/VkBootstrap.cpp, and drops them into your project. The project states plainly that this is not a header only library, so there is no concern about macros leaking from the header into your translation units, but the flip side is that one .cpp file has to be compiled as part of your build.

The git submodule route adds the repository as a submodule and includes it with add_subdirectory, then links it by target name:

```bash
git submodule add https://github.com/charles-lunarg/vk-bootstrap
```

```cmake
add_subdirectory(vk-bootstrap)
target_link_libraries(your_application_name vk-bootstrap::vk-bootstrap)
```

The FetchContent route needs cmake 3.12 or later and does the download and build for you:

```cmake
include(FetchContent)
FetchContent_Declare(
    fetch_vk_bootstrap
    GIT_REPOSITORY https://github.com/charles-lunarg/vk-bootstrap
    GIT_TAG        BRANCH_OR_TAG #suggest using a tag so the library doesn't update whenever new commits are pushed to a branch
)
FetchContent_MakeAvailable(fetch_vk_bootstrap)
target_link_libraries(your_application_name vk-bootstrap::vk-bootstrap)
```

The comment on GIT_TAG is a genuine piece of advice, since pinning to a moving branch means a fresh clone can pull in a commit you never tested. The exported target name vk-bootstrap::vk-bootstrap is the same in both CMake routes, so switching between them does not touch your link lines.

## The dynamic linker requirement on Unix

There is one platform constraint that catches people out. The library has no external dependencies beyond C++17, its standard library, and at least version 1.1 of the Vulkan Headers, and on Unix platforms it requires the dynamic linker in order to compile, because the library does not link against vulkan-1.dll or libvulkan.so directly.

The consequence is that on Unix the library loads the required Vulkan symbols at runtime, and how the dynamic linker is linked into the project depends on the build system. With CMake, the instruction is to link vk-bootstrap with ${CMAKE_DL_LIBS}. A build that skips this step fails at link time, and the error message points at the dynamic linker rather than at anything in the Vulkan setup, which is not a helpful place to start debugging.

The upside of the runtime loading approach is that the binary does not hard-depend on a particular Vulkan loader at build time, which is convenient across the range of machines an application has to run on. The downside is a build configuration step that the documentation mentions in a single line under a Linux specific heading, so it is worth reading before the first build rather than after the first link error.

## Build options, header discovery, and running the tests

Three CMake options control the build, and all three default to off or empty. VK_BOOTSTRAP_WERROR turns warnings into errors during compilation, which is worth enabling in a CI pipeline. VK_BOOTSTRAP_TEST enables building the tests, and it pulls in GLFW and Catch2 automatically through CMake fetch content when turned on. VK_BOOTSTRAP_VULKAN_HEADER_DIR is a string option that points at a directory containing the Vulkan Headers.

That third option matters because of the default behaviour. By default, when the library is used through CMake, it tries to locate the Vulkan-Headers on the system and falls back to downloading them directly if they are not present. If your build machine is offline, or if you want the headers pinned to a known version, you set the option and no download happens. A build that silently reaches out to the network for headers is the kind of thing that breaks a hermetic pipeline.

The tests are enabled automatically when the project is opened standalone, and forced on when it is included as a subdirectory or sub-project, in which case the option is passed explicitly:

```bash
cmake ../path/to/your_project/ -DVK_BOOTSTRAP_TEST=ON
```

The repository layout supports all of this: a CMakeLists.txt at the root, src/ for the library sources, an example/ directory with a CMakeLists.txt of its own and the shaders subdirectory, tests/, docs/ for the getting started guide, and an ext/ and gen/ directory at the top level that the documentation does not describe in detail.

## Licensing, release history, and what to check before you depend on it

The licence is MIT, with a LICENSE.txt file at the repository root. For a library that is often vendored or embedded into a larger engine, that matters, and the copy-paste route makes vendoring a first-class option rather than a workaround.

The repository has no GitHub releases, so there is no tagged version list to choose between in the releases page, and the FetchContent example in fact suggests pinning to a tag or branch explicitly. The absence of tagged releases means you should decide on your own pinning policy, whether that is a commit hash in a submodule or a tag in FetchContent, and record it somewhere visible in your build configuration.

The last push was on 2026-10-07, so the code is current. What is missing is the surrounding evidence: no changelog, no upgrade guide, and no compatibility statement beyond the stated minimum of Vulkan Headers version 1.1.

The honest summary is that this is a small, focused utility with a narrow and well-defined job, and the risk of adopting it is low if you pin your version and read the getting started guide in docs/getting_started.md before you write the first line of setup code.

## Conclusion

vk-bootstrap is a good fit for a C++ team starting a new Vulkan application that does not want to hand-write instance and device enumeration, because the builder API and the error-carrying result objects remove a large amount of repetitive code, and the MIT licence keeps it easy to vendor; it is the wrong choice if you need a header only library, if you are targeting a Vulkan version below 1.1, or if your build system cannot accommodate a separately compiled translation unit, and before adopting it you should check that the Vulkan-Headers on your system are at least version 1.1, since that is the stated minimum and the CMake path will otherwise download headers on your behalf.

## FAQ

### How do I add vk-bootstrap to a CMake project?

Use either a git submodule with add_subdirectory(vk-bootstrap) or CMake FetchContent, which needs cmake 3.12 or later. In both cases you link the target vk-bootstrap::vk-bootstrap through target_link_libraries.

### Is vk-bootstrap a header only library?

No. The project states that it is not a header only library, so there are no macros to worry about in the header, but you either copy src/VkBootstrap.h, src/VkBootstrapDispatch.h and src/VkBootstrap.cpp into your project or compile it as a separate target.

### What are the build requirements for vk-bootstrap?

C++17, its standard library, and at least version 1.1 of the Vulkan Headers. On Unix the library loads Vulkan symbols at runtime, so the dynamic linker has to be linked in, which with CMake means linking vk-bootstrap with ${CMAKE_DL_LIBS}.

### How do I select a specific GPU with vk-bootstrap?

You use the physical device selector and state your requirements instead of relying on enumeration order. The example sets a surface, calls set_minimum_version and require_dedicated_transfer_queue, and the optional configuration allows selecting a GPU by features, extensions and memory.

### How do I run the vk-bootstrap tests?

Tests are enabled automatically when the project is opened standalone. When it is included as a subdirectory you can force them on with the VK_BOOTSTRAP_TEST option, for example cmake ../path/to/your_project/ -DVK_BOOTSTRAP_TEST=ON, and GLFW and Catch2 are fetched automatically.

## Sources

- [charles-lunarg/vk-bootstrap on GitHub](https://github.com/charles-lunarg/vk-bootstrap)
- [Issues](https://github.com/charles-lunarg/vk-bootstrap/issues)
- [License: MIT](https://github.com/charles-lunarg/vk-bootstrap/blob/main/LICENSE)
- [README](https://github.com/charles-lunarg/vk-bootstrap/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/charles-lunarg-vk-bootstrap
