How to export every version of a Vagrant box from HCP Vagrant and host it yourself
By Factodus · updated
HCP Vagrant stops serving boxes on 31 December 2026 (why and when). The Vagrant Cloud API still answers until then, and it lists every version of a box with its download links, so you can copy everything out with curl and jq and serve it from any static host.
Short version. Run the script below in an empty folder for each box. It downloads all versions, providers and architectures, computes sha256 checksums, and writes
metadata.json. Upload the folder, then setconfig.vm.box_urlto the uploadedmetadata.json. We tested it with Vagrant 2.4.9: a pinned version and the latest version both install from a plain HTTP server.
The script
Needs bash, curl, jq and sha256sum (Linux, macOS with coreutils, or WSL).
#!/usr/bin/env bash
# Usage: export-hcp-box.sh <org/name> <base URL where this folder will be served>
set -euo pipefail
box=$1 base=${2%/}
auth=()
if [ -n "${VAGRANT_CLOUD_TOKEN:-}" ]; then auth=(-H "Authorization: Bearer $VAGRANT_CLOUD_TOKEN"); fi
curl -fsS "${auth[@]}" "https://vagrantcloud.com/api/v2/box/$box" > box.json
: > sums.txt
jq -r '.versions[] | .version as $v | .providers[] | [$v, .name, .architecture, .download_url] | @tsv' box.json |
while IFS=$'\t' read -r v p a url; do
mkdir -p "$v/$p"
curl -fL --retry 3 "${auth[@]}" -o "$v/$p/$a.box" "$url"
echo "$v/$p/$a $(sha256sum "$v/$p/$a.box" | cut -d' ' -f1)" >> sums.txt
done
jq --arg base "$base" --rawfile sums sums.txt '
($sums | split("\n") | map(select(length > 0) | split(" ") | {key: .[0], value: .[1]}) | from_entries) as $sha
| {name: .tag, versions: [.versions[] | .version as $v | {version: $v, providers: [.providers[] | {
name, architecture, default_architecture,
url: "\($base)/\($v)/\(.name)/\(.architecture).box",
checksum_type: "sha256", checksum: $sha["\($v)/\(.name)/\(.architecture)"]}]}]}' box.json > metadata.json
echo "wrote metadata.json for $(jq '.versions | length' metadata.json) versions"
Run it once per box, in a folder named after it:
mkdir -p acme/base && cd acme/base
bash export-hcp-box.sh acme/base https://boxes.example.com/acme/base
For private boxes, export a token first: export VAGRANT_CLOUD_TOKEN=.... For organizations already
moved to HCP, that is an HCP access token; curl does not forward it to the storage host the download link
redirects to.
You get this layout, ready to upload as is:
acme/base/
metadata.json
1.0.0/virtualbox/amd64.box
1.0.0/libvirt/amd64.box
1.1.0/virtualbox/amd64.box
...
box.json (the raw API answer) and sums.txt are only for your records.
Upload and use
Copy the folder to any host that serves files over HTTPS: an S3 or R2 bucket with public read, GitLab Pages, nginx. For example, with the AWS CLI:
aws s3 cp --recursive acme/base s3://my-boxes/acme/base
Then in each Vagrantfile:
config.vm.box = "acme/base"
config.vm.box_url = "https://boxes.example.com/acme/base/metadata.json"
config.vm.box_version = "~> 1.1" # optional, constraints work as before
or by hand: vagrant box add https://boxes.example.com/acme/base/metadata.json. Vagrant reads the name,
versions and checksums from metadata.json and verifies every download against it.
Limits of the static approach
- Every Vagrantfile and CI job needs the
box_url: the short name alone no longer resolves. - No private boxes on a public bucket: Vagrant sends no credentials to a static file host.
- New versions mean regenerating
metadata.json: add the entry by hand or rerun the script against your own copy. - Traffic is billed on most clouds; on AWS S3 about $0.09 per GB after the first 100 GB a month.
If you need short names, private boxes or no Vagrantfile edits, a registry that speaks the Vagrant Cloud
API keeps all of that through VAGRANT_SERVER_URL. That is what BoxHarbor does; its migrator
does the same copy as this script for a whole organization and resumes where it stopped.