Skip to content

vcpkg is Microsoft’s cross-platform package manager. While originally designed with an Imperative, global installation model (“Classic Mode”), modern C++ architecture relies on Manifest Mode.

Manifest Mode shifts dependency management from a system-level state to a project-level state. It Utilizes a declarative JSON file (vcpkg.json) to define the dependency graph, ensuring that Checking out a repository at a specific commit guarantees the exact reproduction of the build Environment, including all third-party library versions.

The core of this architecture is the manifest file located at the project root. It serves a role Analogous to Cargo.toml in Rust or package.json in Node.js, but with mechanics adapted for C++ ABI constraints.

{
"name": "high-perf-system",
"version-string": "1.0.0",
"description": "High-performance data processing system",
"builtin-baseline": "a1b2c3d4e5f6789012345678901234567890abc",
"dependencies": [
"fmt",
"nlohmann-json",
"spdlog",
{
"name": "asio",
"version>=": "1.24.0"
},
{
"name": "gtest",
"host": true
},
{
"name": "protobuf",
"host": true,
"default-features": false,
"features": ["protoc"]
}
],
"overrides": [
{
"name": "fmt",
"version": "10.1.1"
}
],
"features": {
"networking": {
"description": "Networking support via asio",
"dependencies": ["asio"]
},
"testing": {
"description": "Testing support via gtest",
"dependencies": ["gtest"]
}
},
"vcpkg-configuration": {
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "a1b2c3d4e5f6789012345678901234567890abc"
}
}
}
  1. Builtin-Baseline: vcpkg does not resolve versions by checking the “latest” available on the server. Instead, it uses a Baseline, which is a specific Git commit SHA of the vcpkg repository. The baseline defines a snapshot of the entire C++ ecosystem that is known to compile together.
  2. Dependencies: Simple strings denote the package name. The version is implicitly determined by the baseline.
  3. Constraints (version>=): These enforce a minimum version. If the baseline provides an older version, vcpkg upgrades.
  4. Host Dependencies: The "host": true flag indicates a tool required for the build process itself (e.g., protobuf compiler) rather than a library to link against. This allows cross-compilation where the build host (x64) runs tools to generate code for the target (ARM64).
  5. Overrides: These force a specific version, bypassing the baseline logic. This is commonly used to pin exact versions or temporarily revert a broken update.
  6. Features: Named groups of dependencies that can be optionally installed. Enable with --x-feature=networking.
  7. Features on dependencies: Control which features of a dependency are built. This reduces compile time by excluding unused functionality.

The baseline is the foundational version pinning mechanism. When you specify a baseline commit:

  1. Vcpkg checks out that exact commit of its own repository.
  2. For each dependency, vcpkg reads the versions/<baseline>/<package>/baseline.json file at that commit, which specifies the exact version of the package.
  3. All packages are built at the versions specified by the baseline, ensuring a known-good combination.

If you need to update a dependency, you either change the baseline (updating all dependencies to the New baseline’s versions) or use an override to pin a specific package to a different version.

Large-scale systems often require dependencies not present in the public Microsoft registry, such as Proprietary internal libraries or forks of open-source projects. Registry Federation allows a Project to compose dependencies from multiple disparate sources transparently.

This is configured via vcpkg-configuration.jsonWhich sits alongside the manifest.

A vcpkg Registry is a Git repository containing:

  1. Ports: Scripts (portfile.cmake) describing how to download and build a package.
  2. Versions: A database mapping version numbers to Git tree objects.

File: vcpkg-configuration.json

{
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "a1b2c3d4e5f6789012345678901234567890abc"
},
"registries": [
{
"kind": "git",
"repository": "https://github.com/my-company/private-cpp-registry",
"baseline": "f9e8d7c6b5a493827162639401827364a5b6c7d8",
"packages": ["internal-logger", "proprietary-math"]
}
]
}
  1. Vcpkg parses the packages array of every defined registry.
  2. If a dependency matches a package listed in a custom registry (e.g., internal-logger), vcpkg resolves it against that specific Git repository.
  3. All other packages fall back to the default-registry.

This federation allows seamless mixing of public open-source libraries and private proprietary Components within a single build graph.

Vcpkg integrates into the build system via a CMake Toolchain File. This file intercepts find_package() calls and redirects them to the vcpkg-installed artifacts.

To ensure reproducibility, vcpkg itself should be bootstrapped locally (often as a Git submodule) Rather than relying on a global installation.

Terminal window
git submodule add https://github.com/microsoft/vcpkg.git external/vcpkg
./external/vcpkg/bootstrap-vcpkg.sh # (or .bat on Windows)

The integration occurs at the configuration step. You must define CMAKE_TOOLCHAIN_FILE.

Command Line:

Terminal window
cmake -S . -B build \
-DCMAKE_TOOLCHAIN_FILE=external/vcpkg/scripts/buildsystems/vcpkg.cmake

CMake Presets (Recommended):

In CMakePresets.jsonSet the cache variable and the environment variable to toggle Manifest Mode.

{
"configurePresets": [
{
"name": "vcpkg-base",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/external/vcpkg/scripts/buildsystems/vcpkg.cmake",
"VCPKG_TARGET_TRIPLET": "x64-linux"
}
}
]
}

Once configured, usage in CMakeLists.txt follows standard CMake patterns. Vcpkg ensures the Libraries are available on the search path.

find_package(fmt CONFIG REQUIRED)
target_link_libraries(App PRIVATE fmt::fmt)

The vcpkg toolchain file modifies CMake’s module search path (CMAKE_PREFIX_PATH) to include the vcpkg_installed/ directory. When find_package(fmt) is called, CMake searches for vcpkg_installed/x64-linux/share/fmt/fmtConfig.cmake (or the Find module). If found, the package is Available. If not found, CMake falls back to system-wide installations.

This mechanism is transparent to the project — standard CMake find_package calls work without Modification.

Unlike package managers that allow nested dependencies (Node.js), vcpkg enforces a flat dependency Graph. If Lib A needs fmt 9.0 and Lib B needs fmt 10.0Vcpkg calculates a single version (10.0) for the entire graph and recompiles Lib A against it. This prevents One Definition Rule (ODR) violations and runtime ABI crashes.

Vcpkg uses Triplets (e.g., x64-windows-static``arm64-linux) to define the build Configuration for all dependencies. This allows an architect to switch the entire dependency tree From Dynamic Linking to Static Linking, or change the CRT linkage, by modifying a single variable (VCPKG_TARGET_TRIPLET).

Because vcpkg builds from source (unless Binary Caching is active), the resulting binaries are Guaranteed to use the exact compiler flags (-O3``-fsanitize=address) of the parent project, Ensuring complete binary compatibility.

In classic mode, vcpkg operates as a global package manager. Packages are installed into a shared Directory and selected via environment variables.

Terminal window
# Classic mode (legacy, not recommended)
vcpkg install fmt:x64-windows
vcpkg integrate install # modifies system-wide CMake settings

Problems with Classic Mode:

  • Global state: Multiple projects share the same vcpkg installation, leading to version conflicts.
  • Non-reproducible: vcpkg install fmt installs the latest version, which changes over time.
  • No dependency graph isolation: Each project’s dependencies bleed into each other.

Manifest mode (enabled by default since vcpkg 2022.11.12) ties dependencies to the project via vcpkg.json in the project root. This is the only mode discussed in this document.

Terminal window
# Manifest mode (automatic when vcpkg.json exists)
vcpkg install # reads vcpkg.json, installs to vcpkg_installed/

A triplet is a string that defines the target platform, architecture, and linkage model. Vcpkg Uses triplets to control how every dependency is built.

TripletOSArchitectureLinkageCRT
x64-windowsWindowsx86_64Dynamic (DLL)MD/MDd
x64-windows-staticWindowsx86_64Static (LIB)MT/MTd
x64-linuxLinuxx86_64Dynamic (SO)default
x64-linux-staticLinuxx86_64Static (A)default
arm64-windowsWindowsARM64Dynamic (DLL)MD/MDd
arm64-linuxLinuxARM64Dynamic (SO)default
x64-mingw-dynamicWindows (MinGW)x86_64Dynamicdefault
wasm32-emscriptenWebAssemblywasm32StaticN/A
Terminal window
# Via environment variable
export VCPKG_TARGET_TRIPLET=x64-linux-static
# Via CMake
cmake -S . -B build \
-DCMAKE_TOOLCHAIN_FILE=external/vcpkg/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=x64-linux-static

For embedded or specialized targets, create a custom triplet file:

File: triplets/my-custom-linux.cmake

set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Linux)
# Pass additional CMake variables to all port builds
set(VCPKG_CMAKE_CONFIGURE_OPTIONS
-DENABLE_NEON=ON
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
)
Terminal window
vcpkg install fmt:my-custom-linux

Edit vcpkg.json and add the package name. Then reconfigure CMake:

Terminal window
# vcpkg.json already updated with "spdlog"
cmake --build build # triggers vcpkg to download and build spdlog

Or use the CLI (modifies vcpkg.json automatically):

Terminal window
vcpkg add spdlog
Terminal window
# Install all dependencies from vcpkg.json
vcpkg install
# Install for a specific triplet
vcpkg install --triplet arm64-linux
Terminal window
vcpkg remove spdlog

Or manually remove the entry from vcpkg.json and reconfigure.

Terminal window
# Check what can be updated
vcpkg update
# Update all dependencies to their latest versions within the baseline
vcpkg upgrade
# Update and rebuild everything (aggressive, may break)
vcpkg upgrade --no-dry-run
Terminal window
vcpkg search json
# Output:
# nlohmann-json 3.11.3 JSON for Modern C++
# json-c 0.17 JSON manipulation in C
# rapidjson 1.1.0 Fast JSON parser/generator
Terminal window
vcpkg list

Export the set of installed packages and their versions for auditing or deployment:

Terminal window
vcpkg export --output=vcpkg-export fmt nlohmann-json

When a library is not available in the public vcpkg registry, or you need a custom fork, you can use overlay ports. An overlay port is a local directory containing a portfile that takes priority Over the registry.

Project/
├── vcpkg.json
├── vcpkg-overlays/
│ └── my-custom-lib/
│ ├── portfile.cmake
│ └── vcpkg.json

vcpkg-overlays/my-custom-lib/portfile.cmake:

vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO mycompany/custom-lib
REF v2.1.0
SHA512 abc123...
)
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
OPTIONS
-DBUILD_TESTS=OFF
)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup()
Terminal window
cmake -S . -B build \
-DCMAKE_TOOLCHAIN_FILE=external/vcpkg/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_OVERLAY_PORTS=vcpkg-overlays

Vcpkg resolves my-custom-lib from the overlay directory first, falling back to the public registry For all other packages.

Overlay ports take precedence over registry ports. If an overlay defines a port with the same name As a registry port, the overlay version is used. This allows you to:

  1. Fork a library and use the fork instead of the upstream version.
  2. Apply patches to an existing port without modifying the vcpkg registry.
  3. Prototype a port before contributing it upstream.

Vcpkg packages binary artifacts as NuGet packages for storage and distribution. This is the most Common binary caching backend in enterprise environments.

Terminal window
# Linux/macOS
export VCPKG_BINARY_SOURCES="clear;nuget,https://artifactory.example.com/nuget/vcpkg-cache,readwrite"
# Windows
$env:VCPKG_BINARY_SOURCES="clear;nuget,https://artifactory.example.com/nuget/vcpkg-cache,readwrite"
  1. After building a package, vcpkg zips the artifacts (include/``lib/``bin/).
  2. It packages the zip as a NuGet package with the ABI hash as the version.
  3. It pushes the package to the configured NuGet feed.
  4. On subsequent builds, vcpkg queries the feed for a matching ABI hash before compiling.

The ABI hash includes:

  • Package name and version
  • Target triplet
  • Compiler version and flags
  • Features enabled
  • All transitive dependency versions and hashes

This ensures that a cached binary is only used when the exact same build configuration is in effect.

For a single developer machine, a local filesystem cache avoids redundant rebuilds across projects:

Terminal window
export VCPKG_BINARY_SOURCES="clear;files,$HOME/.cache/vcpkg,readwrite"

Every vcpkg port is defined by a portfile.cmake that describes how to download, build, and install The package:

# portfile.cmake structure (simplified)
# 1. Download the source
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO fmtlib/fmt
REF 10.1.1
SHA512 <sha512-hash-of-archive>
HEAD_REF master
)
# 2. Configure with CMake
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
OPTIONS
-DFMT_DOC=OFF
-DFMT_TEST=OFF
)
# 3. Build and install
vcpkg_cmake_install()
# 4. Fix up CMake config files for find_package compatibility
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/fmt)
# 5. Copy usage documentation
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
CommandPurpose
vcpkg_from_githubDownload source from GitHub
vcpkg_from_gitlabDownload source from GitLab
vcpkg_from_sourceforgeDownload source from SourceForge
vcpkg_download_distfileDownload an arbitrary file (by URL)
vcpkg_cmake_configureRun CMake configure step
vcpkg_cmake_buildRun CMake build step
vcpkg_cmake_installRun CMake install step
vcpkg_cmake_config_fixupFix CMake config files for correct installation
vcpkg_install_copyrightInstall license/copyright files

Organizations with proprietary libraries can host private vcpkg registries. A registry is a Git Repository containing port definitions and version metadata.

private-registry/
├── ports/
│ ├── internal-logger/
│ │ ├── portfile.cmake
│ │ └── vcpkg.json
│ └── proprietary-math/
│ ├── portfile.cmake
│ └── vcpkg.json
└── versions/
└── internal-logger/
└── baseline.json

In vcpkg-configuration.json:

{
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "a1b2c3d4e5f6789012345678901234567890abc"
},
"registries": [
{
"kind": "git",
"repository": "https://github.com/mycompany/private-cpp-registry",
"baseline": "f9e8d7c6b5a493827162639401827364a5b6c7d8",
"packages": ["internal-logger", "proprietary-math"]
}
]
}

When vcpkg encounters internal-logger in the dependency list, it fetches the port definition from The private registry instead of the public Microsoft registry.

name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Bootstrap vcpkg
run: ./external/vcpkg/bootstrap-vcpkg.sh
- name: Configure
run: |
cmake -S . -B build \
-DCMAKE_TOOLCHAIN_FILE=external/vcpkg/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=x64-linux
- name: Build
run: cmake --build build

Since vcpkg builds from source by default, CI builds can be slow. Enable binary caching to avoid Rebuilding dependencies on every CI run:

- name: Restore vcpkg packages
uses: actions/cache@v3
with:
path: |
vcpkg_installed
~/.cache/vcpkg
key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }}

For self-hosted runners, maintain a persistent vcpkg_installed/ directory. Dependencies are only Rebuilt when vcpkg.json changes:

Terminal window
# On the CI runner, set a persistent vcpkg root
export VCPKG_DEFAULT_TRIPLET=x64-linux
export VCPKG_ROOT=/opt/vcpkg
FeaturevcpkgConan
MaintainerMicrosoftJFrog (open-source core)
Manifest formatvcpkg.json (JSON)conanfile.txt / conanfile.py
Build system integrationCMake toolchain fileCMake generator, toolchain
Dependency graphFlat (ODR-safe)Flat (C++11+) or nested (C++03)
Binary cachingNuGet, S3, filesystemConan Server, Artifactory, S3
Private registriesGit-basedGit-based or Conan Server
Cross-compilationVia tripletsVia profiles and settings
Language supportC/C++ onlyC/C++, plus some cross-language support
Lock filevcpkg.json (implicit via baseline)conan.lock (explicit)
Python APINo (CMake-based portfiles)Yes (full Python API for portfiles)
Community packages~2000+~1800+
  • Your project uses CMake exclusively.
  • You need ODR-safe flat dependency resolution.
  • You want tight integration with Microsoft tooling (Visual Studio, Azure CI).
  • You prefer JSON manifests over Python scripts.
  • Your project uses multiple build systems (CMake, Meson, Bazel).
  • You need complex cross-compilation with multiple profiles.
  • You want a Python-based portfile API for maximum flexibility.
  • You already use JFrog Artifactory in your organization.
  1. Mixing triplets in the same build tree: If you change VCPKG_TARGET_TRIPLET without clearing the build directory, stale artifacts from the previous triplet may cause linker errors. Always delete build/ and vcpkg_installed/ when changing triplets.
  2. Forgetting --triplet for CLI commands: vcpkg install fmt installs the default triplet, which may not match your project’s triplet. Always specify: vcpkg install fmt:x64-linux-static.
  3. Overlay port SHA512 mismatch: When using overlays, the SHA512 in portfile.cmake must match the downloaded archive. If you update the archive without updating the hash, vcpkg will fail with a checksum error.
  4. NuGet mono dependency on Linux/macOS: vcpkg’s NuGet binary cache requires mono on non-Windows platforms. Install it via brew install mono or apt install mono-complete.
  5. Not pinning the vcpkg submodule commit: If you use vcpkg as a submodule, the submodule commit determines which baseline is used. Different developers with different submodule commits will build different dependency versions. Pin the submodule to a specific commit.
  6. Stale vcpkg_installed/ after vcpkg.json changes: When you modify vcpkg.json (add or remove a dependency), the vcpkg_installed/ directory may contain stale artifacts. Delete it and reconfigure: rm -rf vcpkg_installed && cmake --fresh -S . -B build.
  7. find_package failing after vcpkg install: Ensure the toolchain file is passed to every CMake invocation. If you configure without the toolchain file, find_package will not search the vcpkg installation directory.
  8. Host vs target confusion with cross-compilation: When cross-compiling (e.g., x64 host building for ARM64 target), build tools (protoc, flatbuffers compiler) must be built for the host architecture. Use "host": true on these dependencies to ensure correct resolution.

This topic covers the essential concepts and techniques related to vcpkg, including key principles and practical applications.

Key concepts include:

  • core concepts and definitions
  • key principles and frameworks
  • practical applications
  • common techniques and methods
  • evaluation and critical analysis

A thorough understanding of these concepts, combined with regular practice and review, is essential for mastery of this topic.

Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.