Open-source project
denoland/rusty_v8 avatar
denoland/rusty_v8

rusty_v8: Rust bindings to V8 that build the engine from source

Rust bindings for the V8 JavaScript engine

4,041 stars452 forksRustMIT

At a glance

What is it?
rusty_v8 exposes V8's C++ API to Rust and treats compiling V8 as part of cargo build rather than shipping an opaque libv8.a. It is the embedding layer under Deno, and it is a large, release-cycle-driven dependency.
Who is it for?
Adopt rusty_v8 if you are embedding V8 in a Rust program and can accept a dependency that tracks Chrome's four-week release train and, in the from-source path, a build measured in tens of minutes. Do not adopt it if you want a small, slowly moving JavaScript engine, or if your target platform has no published prebuilt static lib and you are not prepared to compile V8 yourself.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • denoland/rusty_v84,041 ★ · +90
Star history
Repository2026-09-242026-11-14
denoland/rusty_v84k4k

90 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What rusty_v8 is for, and who ends up using it

rusty_v8 is a Rust crate named v8 that binds V8's C++ API. The stated goal is that the Rust API should match the original C++ API as closely as possible, without adding call overhead. That second part matters: the README notes that previous attempts at Rust V8 bindings forced the use of Persistent handles, and rusty_v8 treats avoiding that as a design goal rather than an implementation detail.

The audience is narrow and specific. You are writing a Rust program that needs to execute JavaScript, and you need the real engine rather than an interpreter written in Rust. Deno is the obvious case, and the README's defaults are visibly shaped by that: release builds of V8 are the default "due to performance & CI reasons in deno". If you are building a plugin host, a server-side renderer, an editor extension runtime, or a rules engine where the rules arrive as JavaScript, this is the layer you would sit on.

It is not a sandbox and it is not a framework. The crate gives you handles, isolates, contexts and the rest of the C++ surface. Whatever policy you want around untrusted code is yours to build.

How the build decides between a prebuilt lib and compiling V8

The mechanism worth understanding is the build script. Binary builds are the default: cargo build initiates a download from GitHub to fetch a static lib. Setting V8_FROM_SOURCE to 1 (true and yes also work) switches to compiling V8 from source; any other value, or leaving it unset, uses the prebuilt lib. The rationale given is that V8 is over 600,000 lines of C++ and often takes 30 minutes to compile, and that relying on a separately built libv8.a makes upgrading V8, CI, and custom configurations harder, and is a security concern since binary blobs can hide malicious code.

Artifact resolution is an ordered list, and the first candidate that works wins. RUSTY_V8_ARCHIVE (the static lib) or RUSTY_V8_SRC_BINDING_URL (the generated binding file) short-circuits everything else for its own artifact if set. Then the mirror, if RUSTY_V8_MIRROR is set. For plain filesystem mirrors there is a flat layout fallback at <base>/<file>. Finally the upstream GitHub release. With a mirror configured, upstream is only tried when RUSTY_V8_MIRROR_FALLBACK=1 is set, because a mirror fails closed by default and never silently reaches the network. If every candidate fails, the build script panics with the full list of URLs it tried.

The tag defaults to v<version>, the crate version. RUSTY_V8_MIRROR_TAG overrides it and is used verbatim, with no v prepended, so RUSTY_V8_MIRROR_TAG=v152.0.0 cargo build fetches the artifacts of the last published release when the checkout's Cargo.toml version is unpublished. A mirror value containing a { placeholder is treated as a full URL template rather than a base, with {tag}, {version}, {target}, {profile}, {features} and {file} available.

Installing the crate and building against a chosen V8

The crate is published on crates.io as v8, and the README points at docs.rs for generated documentation. The repository's Cargo.toml declares the package name as v8, version 152.2.0, with the description "Rust bindings to V8".

With the dependency in place, cargo build downloads the prebuilt static lib from the GitHub releases unless you opt into a source build. To compile V8 yourself instead, export the variable before building. The README notes that changes to rusty_v8 itself should be tested this way, and that CI always builds from source.

bash
export V8_FROM_SOURCE=1
cargo build

The README gives a second example for pinning the mirror tag, which is useful when the checkout's version is unpublished and you want the artifacts of the last published release:

bash
RUSTY_V8_MIRROR_TAG=v152.0.0 cargo build

The repository ships runnable examples, including examples/hello_world.rs and examples/shell.rs, so you do not have to write the isolate setup from scratch before seeing anything work. What you should see is JavaScript evaluated inside V8 from a Rust binary, with the crate's build step having resolved an artifact first. If that step cannot find one, the panic lists every URL it tried, which is the fastest way to tell whether your platform has a published lib or whether you are pointed at a mirror that is missing the file.

The cache, the mirror, and what is not verified

Before downloading, the build script looks in a .rusty_v8 directory inside your Cargo home, usually ~/.cargo/.rusty_v8. Entries are keyed on the release tag plus the artifact filename, with every non-alphanumeric character replaced by an underscore. The README's example: v152.1.0/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz becomes v152_1_0_librusty_v8_release_x86_64_unknown_linux_gnu_a_gz. The escaped full source URL, the key used by older versions of the build script, is still checked as a fallback so existing caches keep working.

The consequence is stated plainly in the README: because the key does not include the source, a cache entry populated for one mirror also satisfies a build configured for a different mirror, or for the upstream release, under the same tag. That is a deliberate trade in favour of cache reuse, and it means the cache is not a place where provenance survives. The documented remedy is to pin the archive bytes with RUSTY_V8_ARCHIVE_SHA256. If you care about which bytes you link, that variable is the control, not the mirror setting.

File mirrors are the supported way to work from cached downloads. The README's recipe sets RUSTY_V8_MIRROR to a local directory, then loops over releases and files, fetching librusty_v8_release_x86_64-unknown-linux-gnu.a.gz and src_binding_release_x86_64-unknown-linux-gnu.rs with wget only when the local copy is absent. That is a workable offline and air-gapped arrangement, provided you pre-populate it for every tag and target you build.

bash
#!/bin/bash

# see https://github.com/denoland/rusty_v8/releases

for REL in v152.1.0 v152.0.0; do
  mkdir -p $RUSTY_V8_MIRROR/$REL
  for FILE in \
    librusty_v8_release_x86_64-unknown-linux-gnu.a.gz \
    src_binding_release_x86_64-unknown-linux-gnu.rs \
  ; do
    if [ ! -f $RUSTY_V8_MIRROR/$REL/$FILE ]; then
      wget -O $RUSTY_V8_MIRROR/$REL/$FILE \
        https://github.com/denoland/rusty_v8/releases/download/$REL/$FILE
    fi
  done
done

Versioning means upgrades arrive on Chrome's schedule

The major version aligns with Chrome's major version, which corresponds to a specific V8 release. Rusty V8 129.0.0 maps to Chrome 129.x.y.z, which uses V8 12.9.a.b. Minor and patch numbers may differ between Chrome and V8, but rusty_v8 follows Chrome's release schedule with a new major version every four weeks. The recent releases bear this out: v152.0.0 on 2026-08-01, v152.1.0 on 2026-08-07, v152.2.0 on 2026-08-20, and the README pins V8 Version 15.2.124.1.

Within a major version the crate follows semantic versioning and will not introduce breaking changes. That is a real guarantee, and it is also the whole of it: major bumps happen regularly by design, so a dependency on v8 is a dependency that will ask you to move every four weeks if you want current V8. There is no long-term support line described in the README.

The practical cost sits in CI. If you build from source, every one of those bumps is a fresh V8 compile unless you cache aggressively, and the repository's own Dockerfile shows what that looks like: it installs Chromium's build dependencies via install-build-deps.sh with --no-prompt --no-chromeos-fonts, and wires in sccache through SCCACHE and SCCACHE_DIR. That is a supported path, but it is a build system, not a crate you drop in.

When rusty_v8 is the wrong tool

The clearest mismatch is size and portability. The prebuilt path depends on a published static lib for your target, and the README does not enumerate which triples are covered. If your platform is not among the release assets, your options are compiling V8 from source or supplying your own archive through RUSTY_V8_ARCHIVE, and neither is a small amount of work. The examples directory hints at the shape of the problem: it contains examples/android/, examples/ios_jitless.rs and examples/cppgc.rs, which suggests mobile and JIT-restricted targets are handled through specific configurations rather than by the default path.

A second mismatch is anyone who wants a small, auditable, quickly built engine. V8 is the opposite of that by construction, and rusty_v8 deliberately refuses to hide it behind a prebuilt blob. If your JavaScript needs are modest and your build budget is not, a smaller engine is the honest choice.

A third is API stability against the C++ surface. The stated goal is to match V8's C++ API as closely as possible, which means the Rust surface inherits the shape and the churn of the underlying engine. If you wanted a Rust-idiomatic wrapper that smooths over V8's handle model, this is not it; it is a binding, and it says so.

Alternatives and the difference in approach

The README itself frames the history: many attempts to bind V8 rely on pre-built binaries built separately from the binding, which is simple but makes upgrading V8 difficult, makes CI difficult, makes producing builds with different configurations difficult, and hides binary blobs. rusty_v8's answer is to build V8 from source during cargo build, with the prebuilt download as a convenience default. That is the axis to compare on. A binding that ships a vendored libv8.a will install faster and will not need Chromium's build dependencies, and in exchange you take whatever V8 version and configuration the packager chose.

Going further out, the alternatives are different engines rather than different bindings. QuickJS and JavaScriptCore are separate implementations with their own Rust bindings, and they are the realistic options when the constraint is binary size, build time or target coverage rather than exact V8 semantics. The trade is behavioural: code that depends on V8-specific behaviour, on its performance characteristics, or on the specific C++ API surface will not port cleanly to a smaller engine. Choosing between them is really choosing whether you need V8 or merely need JavaScript.

One thing rusty_v8 does that a separate-binary binding usually cannot: because the build script resolves artifacts per tag and target and can compile from source, you can produce builds with different configurations from the same crate. That flexibility is the point of the design, and it is also why the build story is more involved than a single dependency line.

Editorial conclusion

Adopt rusty_v8 if you are embedding V8 in a Rust program and can accept a dependency that tracks Chrome's four-week release train and, in the from-source path, a build measured in tens of minutes. Do not adopt it if you want a small, slowly moving JavaScript engine, or if your target platform has no published prebuilt static lib and you are not prepared to compile V8 yourself. Before committing, verify that your target triple appears in the release assets, decide whether you need V8_FROM_SOURCE=1, and set RUSTY_V8_ARCHIVE_SHA256 if you cannot trust the mirror you fetch from.

Frequently asked questions

Does rusty_v8 compile V8 from source during cargo build?

Not by default. Binary builds are the default, and cargo build initiates a download from GitHub to get the static lib. Setting V8_FROM_SOURCE to 1, true or yes makes the build compile V8 from source instead.

How do I make rusty_v8 use a local mirror instead of GitHub?

Set RUSTY_V8_MIRROR to a URL base or a filesystem path. With a mirror configured the upstream GitHub release is only tried when RUSTY_V8_MIRROR_FALLBACK=1 is set, because a mirror fails closed by default.

How often does rusty_v8 release new major versions?

The major version aligns with Chrome's major version and follows Chrome's release schedule, with a new major version every four weeks. Minor and patch numbers between Chrome and V8 may differ.

What is rusty_v8 written in?

The repository's primary language is Rust, and the crate binds V8's C++ API. The stated goal is that the Rust API should match the original C++ API as closely as possible.

Official sources

  1. denoland/rusty_v8 on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/denoland-rusty-v8.svg)](https://hysenlabs.com/projects/denoland-rusty-v8)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/denoland-rusty-v8.svg)](https://hysenlabs.com/projects/denoland-rusty-v8)

Was this useful?