vcpkg
Microsoft's C++ package manager — manifest mode, classic mode, CMake integration, triplets, overlays, and binary caching.
TL;DR
vcpkg is a cross-platform C++ package manager from Microsoft. Manifest mode (recommended) keeps your dependencies in a vcpkg.json next to your CMakeLists.txt. CMake integration is seamless — packages just work after find_package().
git clone https://github.com/microsoft/vcpkg
./vcpkg/bootstrap-vcpkg.sh # Linux/macOS
# or: .\vcpkg\bootstrap-vcpkg.bat (Windows)Manifest mode (recommended)
Create vcpkg.json in your project root:
{
"name": "my-project",
"version": "1.0.0",
"dependencies": [
"fmt",
"nlohmann-json",
"boost-algorithm",
{
"name": "openssl",
"version>=": "3.0.0"
}
]
}Tell CMake where vcpkg is:
# CMakeLists.txt — before project()
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "vcpkg toolchain")
cmake_minimum_required(VERSION 3.25)
project(my-project)
find_package(fmt CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
find_package(OpenSSL REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
fmt::fmt
nlohmann_json::nlohmann_json
OpenSSL::SSL
)# vcpkg installs dependencies automatically during cmake configure
cmake -B build -DVCPKG_ROOT=/path/to/vcpkg
cmake --build buildSet VCPKG_ROOT environment variable permanently to avoid repeating it:
export VCPKG_ROOT=/home/user/vcpkg # ~/.bashrc or ~/.zshrcClassic mode
Install packages globally into the vcpkg installation:
vcpkg install fmt nlohmann-json boost-algorithm
vcpkg install openssl:x64-linux # explicit triplet
vcpkg list # show installed packages
vcpkg remove fmt # uninstall
vcpkg upgrade # update allClassic mode is useful for quick experiments but manifest mode is preferred for reproducible builds.
Triplets — platform/compiler targets
Triplets specify the target architecture, OS, and linkage:
# Common triplets
x64-linux # x86-64, Linux, dynamic
x64-linux-release # x86-64, Linux, release static
x64-windows # x86-64, Windows, dynamic (MSVC)
x64-windows-static # x86-64, Windows, static (MSVC)
x64-mingw-dynamic # x86-64, Windows, MinGW
arm64-osx # Apple Silicon macOS
arm-neon-android # Android ARM
# In vcpkg.json — set default triplet
{
"name": "my-project",
"dependencies": ["fmt"],
"vcpkg-configuration": {
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "abc123..."
}
}
}# Force static linking
set(VCPKG_TARGET_TRIPLET "x64-linux-release" CACHE STRING "")Binary caching
vcpkg can cache compiled packages to avoid recompiling on every CI run:
# Local filesystem cache (default location on Linux)
~/.cache/vcpkg/archives/
# Custom cache location
export VCPKG_DEFAULT_BINARY_CACHE=/mnt/cache/vcpkg
# GitHub Actions cache
- uses: actions/cache@v4
with:
path: ~/.cache/vcpkg
key: vcpkg-${{ hashFiles('vcpkg.json') }}Versioning and baselines
Manifest mode supports exact version pinning:
{
"name": "my-project",
"dependencies": [
{ "name": "fmt", "version>=": "10.1.0" },
{ "name": "boost-filesystem", "version>=": "1.83.0" }
],
"overrides": [
{ "name": "fmt", "version": "10.1.1" }
],
"builtin-baseline": "a42af01b72c28a8e1d7b48107b33e4f286a55ef6"
}The builtin-baseline pins the entire registry to a specific git commit, ensuring reproducible builds.
# Update baseline to latest
vcpkg x-update-baselineCustom overlays
Add packages not in the vcpkg registry:
project/
├── CMakeLists.txt
├── vcpkg.json
└── overlays/
└── ports/
└── my-private-lib/
├── portfile.cmake
└── vcpkg.json// vcpkg.json — reference the overlay
{
"name": "my-project",
"dependencies": ["my-private-lib"],
"vcpkg-configuration": {
"overlay-ports": ["./overlays/ports"]
}
}Common packages and their CMake target names
| vcpkg package | find_package() | Target |
|---|---|---|
fmt | fmt | fmt::fmt |
nlohmann-json | nlohmann_json | nlohmann_json::nlohmann_json |
boost-filesystem | Boost COMPONENTS filesystem | Boost::filesystem |
openssl | OpenSSL | OpenSSL::SSL OpenSSL::Crypto |
sqlite3 | unofficial-sqlite3 | unofficial::sqlite3::sqlite3 |
catch2 | Catch2 | Catch2::Catch2WithMain |
gtest | GTest | GTest::gtest_main |
spdlog | spdlog | spdlog::spdlog |
protobuf | Protobuf | protobuf::libprotobuf |
zlib | ZLIB | ZLIB::ZLIB |
CI integration (GitHub Actions)
- name: Setup vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgDirectory: ${{ github.workspace }}/vcpkg
vcpkgGitCommitId: 'a42af01b72c28a8e1d7b48107b33e4f286a55ef6'
- name: Configure CMake
run: cmake -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
- name: Build
run: cmake --build buildOr using the built-in GitHub-hosted vcpkg:
- name: Install vcpkg deps
run: vcpkg install --triplet x64-linux
env:
VCPKG_ROOT: /usr/local/share/vcpkgWriting a Custom Port (overlay port)
Ship your own library through vcpkg's install machinery. An overlay port is a directory with two files: portfile.cmake (the build recipe) and vcpkg.json (the port's own manifest). Drop it anywhere in your repo and point vcpkg-configuration.json at it — no registry PR required.
# overlays/ports/mylib/portfile.cmake
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO myorg/mylib
REF "v${VERSION}"
SHA512 0a1b2c3d4e5f... # sha512 of the release tarball
HEAD_REF main
)
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
OPTIONS
-DMYLIB_BUILD_TESTS=OFF
-DMYLIB_BUILD_EXAMPLES=OFF
)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/mylib)
vcpkg_copy_pdbs()
# Remove build-tree artifacts that don't belong in the install
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share")
# Install the license
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")For a headers-only library, skip vcpkg_cmake_configure/install entirely and use a direct file(INSTALL ...):
# portfile.cmake — headers-only variant
set(VCPKG_BUILD_TYPE release) # no debug variant needed
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO myorg/myheaderlib
REF "v${VERSION}"
SHA512 deadbeef...
HEAD_REF main
)
file(INSTALL "${SOURCE_PATH}/include/"
DESTINATION "${CURRENT_PACKAGES_DIR}/include")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")The companion vcpkg.json for the port itself declares version metadata:
{
"name": "mylib",
"version": "2.1.0",
"description": "My private C++ library",
"homepage": "https://github.com/myorg/mylib",
"dependencies": [
"fmt",
"nlohmann-json"
]
}Reference the overlay from your project's vcpkg-configuration.json:
{
"default-registry": {
"kind": "builtin",
"baseline": "a42af01b72c28a8e1d7b48107b33e4f286a55ef6"
},
"overlay-ports": ["./overlays/ports"]
}vcpkg Registry — Using a Private Registry
A private registry lets multiple teams share custom ports without committing them to the upstream vcpkg repo. A registry is just a git repository with a specific directory layout. Packages in it are resolved before the built-in registry when listed under "packages".
Point your project at the registry in vcpkg-configuration.json:
{
"default-registry": {
"kind": "builtin",
"baseline": "a42af01b72c28a8e1d7b48107b33e4f286a55ef6"
},
"registries": [
{
"kind": "git",
"repository": "https://github.com/myorg/myvcpkg-registry",
"baseline": "abc123def456abc123def456abc123def456abc1",
"packages": ["mypkg", "another-internal-lib"]
}
]
}The registry repo must follow this layout:
myvcpkg-registry/
├── baseline.json # maps port name → baseline version
├── versions/
│ ├── baseline.json # same content, canonical location
│ ├── m-/
│ │ └── mypkg.json # version history for mypkg
│ └── a-/
│ └── another-internal-lib.json
└── ports/
├── mypkg/
│ ├── portfile.cmake
│ └── vcpkg.json
└── another-internal-lib/
├── portfile.cmake
└── vcpkg.jsonAfter adding or updating a port in the registry, record it with:
# Run from inside the registry repo
vcpkg x-add-version mypkg
vcpkg x-add-version mypkg --overwrite-version # re-record same version after fixes
git add versions/ baseline.json
git commit -m "Add mypkg 2.1.0"
git pushThen update the "baseline" in your project's vcpkg-configuration.json to the new registry commit SHA.
Cross-Compilation Triplets
vcpkg ships built-in triplets for common ARM targets; writing a custom one is a few lines of CMake. The triplet file tells vcpkg which compiler toolchain to use and how to set up CMake for the target system.
Built-in ARM triplets you can use directly:
arm64-linux # AArch64, Linux, dynamic linking
arm-linux # ARMv6/v7, Linux, dynamic linking
arm64-windows # AArch64, Windows (MSVC cross)
arm-neon-android # ARMv7 with NEON, Android NDK
arm64-android # AArch64, Android NDKFor a bespoke embedded or non-standard target, create a custom triplet file:
# triplets/arm64-linux-custom.cmake
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static) # static libs for the target
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
# Point at your cross-compiler
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)If your toolchain is too complex for inline settings, delegate to a full toolchain file via VCPKG_CHAINLOAD_TOOLCHAIN_FILE:
# triplets/arm64-linux-custom.cmake — sysroot variant
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE
"${CMAKE_CURRENT_LIST_DIR}/../cmake/arm64-linux-toolchain.cmake")
set(VCPKG_CMAKE_SYSTEM_NAME Linux)# cmake/arm64-linux-toolchain.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CROSS_ROOT /opt/aarch64-linux-gnu)
set(CMAKE_SYSROOT "${CROSS_ROOT}/sysroot")
set(CMAKE_C_COMPILER "${CROSS_ROOT}/bin/aarch64-linux-gnu-gcc")
set(CMAKE_CXX_COMPILER "${CROSS_ROOT}/bin/aarch64-linux-gnu-g++")
set(CMAKE_STRIP "${CROSS_ROOT}/bin/aarch64-linux-gnu-strip")Then invoke vcpkg and CMake with the custom triplet:
cmake -B build \
-DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=arm64-linux-custom \
-DVCPKG_OVERLAY_TRIPLETS=./triplets
cmake --build buildCaching in CI — GitHub Actions Advanced
NuGet binary caching cuts vcpkg CI times from minutes to seconds by sharing compiled artifacts across jobs and branches. The lukka/run-vcpkg action handles the boilerplate; you supply a NuGet feed URL (GitHub Packages works out of the box) and set VCPKG_BINARY_SOURCES.
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup NuGet credentials
run: |
mono $(vcpkg fetch nuget | tail -1) sources add \
-source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \
-name "github" \
-username "${{ github.actor }}" \
-password "${{ secrets.GITHUB_TOKEN }}" \
-storepasswordincleartext
- name: Setup vcpkg with NuGet binary caching
uses: lukka/run-vcpkg@v11
with:
vcpkgDirectory: ${{ github.workspace }}/vcpkg
vcpkgGitCommitId: 'a42af01b72c28a8e1d7b48107b33e4f286a55ef6'
env:
VCPKG_BINARY_SOURCES: >-
clear;
nuget,https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json,readwrite
VCPKG_FEATURE_FLAGS: binarycaching
- name: Configure
run: |
cmake -B build \
-DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \
-DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --parallelVCPKG_BINARY_SOURCES accepts multiple providers in one string. Common formats:
# Local filesystem (good for single-machine dev)
export VCPKG_BINARY_SOURCES="clear;files,/mnt/vcpkg-cache,readwrite"
# NuGet feed (GitHub Packages, Azure Artifacts, Nexus, etc.)
export VCPKG_BINARY_SOURCES="clear;nuget,https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json,readwrite"
# GH Actions cache + NuGet (belt-and-suspenders for open-source repos)
export VCPKG_BINARY_SOURCES="clear;nuget,https://nuget.pkg.github.com/myorg/index.json,readwrite"The actions/cache approach (shown in the basic CI section above) is simpler but is per-repo and branch-scoped — it won't share artifacts across forks. NuGet caching is cross-repo and persistent, making it the right choice for organisations with multiple repositories consuming the same vcpkg packages.
Azure DevOps equivalent — replace the NuGet source URL and use $(System.AccessToken):
# azure-pipelines.yml
- script: |
mono $(vcpkg fetch nuget | tail -1) sources add \
-source "https://pkgs.dev.azure.com/$(System.TeamFoundationCollectionUri)/_packaging/vcpkg-cache/nuget/v3/index.json" \
-name "azdo" \
-username "build" \
-password "$(System.AccessToken)" \
-storepasswordincleartext
displayName: 'Add NuGet source for vcpkg binary cache'
env:
VCPKG_BINARY_SOURCES: "clear;nuget,azdo,readwrite"
VCPKG_FEATURE_FLAGS: binarycachingvcpkg Features — Optional Dependencies
Features let a port expose opt-in capabilities without forcing every consumer to pay for heavyweight dependencies. You declare them in the port's vcpkg.json; consumers then request them either in vcpkg.json or on the command line.
Declaring features in a port's vcpkg.json:
{
"name": "mylib",
"version": "2.1.0",
"description": "My C++ library with optional backends",
"dependencies": ["fmt"],
"features": {
"cuda": {
"description": "Enable CUDA GPU backend",
"dependencies": ["cuda"]
},
"python": {
"description": "Build Python bindings via pybind11",
"dependencies": ["pybind11"]
},
"tbb": {
"description": "Parallel algorithms via oneTBB",
"dependencies": ["tbb"]
}
}
}Consuming a port's features in your project's vcpkg.json:
{
"name": "my-project",
"dependencies": [
{
"name": "mylib",
"features": ["cuda", "tbb"]
},
"fmt"
]
}In classic mode, request features with bracket syntax:
vcpkg install mylib[cuda,tbb] # install with specific features
vcpkg install mylib[core,cuda,tbb] # "core" is always implicitFeatures compose across the dependency graph — if two packages both request mylib but one wants [cuda] and the other doesn't, vcpkg builds mylib with cuda enabled (features are additive, never subtracted). This means optional features should only add functionality, never change the ABI of the base library.
See Also
- Conan — alternative package manager with Python-based recipes and more granular binary compatibility control
- CPM — lightweight CMake-native dependency manager that fetches sources at configure time via
FetchContent - CMake — the build system vcpkg integrates with; understanding targets and
find_package()is prerequisite knowledge - Cross-Compilation — system-level guide to sysroots, toolchains, and multi-arch Docker images that pair with vcpkg triplets