Build Reproducible Julia Projects with Pkg Environments
Pin Julia package versions with Project.toml and Manifest.toml so builds reproduce across machines.
02 Dec 2025, 12:16 UTC

Start with the dependency problem
A Julia script that runs on your laptop can fail on a colleague's machine or a CI runner when package versions differ. Julia's package manager, Pkg, solves this with two files: Project.toml, which records the packages your project needs, and Manifest.toml, which records the exact resolved versions and content hashes. Committing both files lets anyone rebuild the same environment.
Prerequisites
- Julia 1.6 or later installed and available on your
PATH. - A terminal or command prompt.
- Internet access to download packages from the General registry.
- Git, if you want to track changes (recommended but not required).
Create a project environment
Open a terminal in the directory where you want the project to live. Run:
julia --project=. -e 'using Pkg; Pkg.activate("."); Pkg.instantiate()'
Where to run: your project directory. Permissions: none beyond normal file access. Placeholder: replace . with a path such as ./my-project if you prefer a subdirectory.
This creates Project.toml and Manifest.toml in the current directory. The --project=. flag tells Julia to use that directory as the active environment instead of the default global environment.
Add packages
julia --project=. -e 'using Pkg; Pkg.add("DataFrames")'
Pkg.add updates Project.toml with a dependency entry and rewrites Manifest.toml with the resolved version tree. Do not edit Manifest.toml by hand; Pkg owns that file.
Expected check: open Project.toml and confirm a [deps] section contains the package name and a UUID. Open Manifest.toml and confirm it lists version strings and content hashes.
Risk: adding a package can change transitive dependencies in Manifest.toml, so review the diff before committing.
Commit the environment files
| File | Commit? | Reason |
|---|---|---|
Project.toml | Yes | Declares direct dependencies and compat bounds. |
Manifest.toml | Yes, for applications | Pins exact versions for reproducible installs. |
Manifest.toml | Usually no, for libraries | Library consumers should resolve their own dependency tree. |
Example git command, run in the project root:
git add Project.toml Manifest.toml
git commit -m "Pin reproducible Julia environment"
Rebuild the environment on another machine
Clone the repository, then run:
julia --project=. -e 'using Pkg; Pkg.instantiate()'
Pkg.instantiate downloads and installs every package recorded in Manifest.toml. It does not change Project.toml. If the manifest is missing or outdated, Pkg resolves from Project.toml instead.
Verify the result
- Run
julia --project=. -e 'using Pkg; Pkg.status()'. The list should match the packages you added, with no unexpected entries. - Run
git diff Project.toml Manifest.tomlafter any change to confirm only intended modifications are present. - If your project includes tests, run
julia --project=. -e 'using Pkg; Pkg.test()'from the project directory. - On a clean machine, clone the repo and run
Pkg.instantiate()to confirm the environment rebuilds without network or version errors.
Recover from a broken environment
These operations change state, so back up or commit your files first.
- Corrupt manifest: delete
Manifest.toml, then runPkg.instantiate()to regenerate it fromProject.toml. - Version conflict: run
Pkg.resolve()to recompute the manifest, or tighten compat bounds inProject.tomland resolve again. - Bad commit: check out the previous
Project.tomlandManifest.tomlfrom Git, then runPkg.instantiate().
Limitations
Manifest.tomlis platform-sensitive. A manifest generated on macOS may not install identically on Windows or Linux. Regenerate it on each target platform withPkg.instantiate().- Julia 1.9+ uses lazy artifact loading. Some packages depend on binary artifacts that must be present on the target machine;
Pkg.instantiate()usually handles this, but offline air-gapped systems need artifact pre-staging. - Reproducibility is tied to the Julia major version. Pin the Julia version in CI or documentation alongside the project files.
Quick check before you finish
Run julia --project=. -e 'using Pkg; Pkg.status()' and confirm the active environment is your project directory, not the default depot. If the status shows the global environment instead, the --project flag or Pkg.activate call was not applied.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.