# Offline installation of Julia packages

**URL:** <https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083>\
**Category:** General Usage\
**Tags:** package, installation\
**Created:** [January 25, 2019, 11:31am UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083 "2019-01-25T11:31:59Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![gdestouet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdestouet/32/6175_2.png) [@gdestouet](https://discourse.julialang.org/u/gdestouet)\
**Post date:** [January 25, 2019, 11:32am UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/1 "2019-01-25T11:32:00Z")

</div>

Hello,

I need to install a bunch of packages on an offline computer ( **_it must be disconnected_** ).

I didn’t found any guide that provide a clean way for installing packages offline and maybe someone could help me with that.

I tried this :

1. For each packages, download its github repo ( not with git clone ) and store it somewhere at `local/path/pkg_n`
2. Add the `local/path/pkg_n` to these packages via `Pkg`

But when adding these local path, `Pkg` return this :

> (v1.1) pkg\> add local\path\pkg\_1  
> Updating registry at `~\.julia\registries\General`  
> Updating git-repo `https://github.com/JuliaRegistries/General.git`  
> Cloning git-repo `local\path\pkg_1\`  
> ERROR: Git repository not found at 'local\path\pkg\_1'

So the local path must be a git repo.

Then I tried with `dev` since it will create automatically a repo and update julia environnement `Manifest.toml` and `Project.toml` files.

For packages with a lot of dependencies, I could do this on all the packages in the dependency graph that are not already installed on hope for the best, but I haven’t tried yet.

Does anyone have any idea on how to do this properly ?

Thank you in advance !

---

<div class="post-metadata">

**Author:** ![dmolina](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmolina/32/5246_2.png) [@dmolina](https://discourse.julialang.org/u/dmolina)\
**Post date:** [January 25, 2019, 11:49am UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/2 "2019-01-25T11:49:17Z")

</div>

I think currently there is no way of doing that, see

> [@Package install on (offline) Cluster - Build needs internet](https://discourse.julialang.org/t/package-install-on-offline-cluster-build-needs-internet/19862):
>
> Hey everyone, I’m trying to use julia 1.0 on our cluster. It is not connected to the internet (login via login-server) so I can’t use the standard procedure to install the packages I need. I read [here](https://discourse.julialang.org/t/offline-package-installation/7880) that one option should be able to set up the .julia folder on a separate computer and copy it to the cluster. (But maybe that was only true for pre-1.0?) I tried it but got the following error ERROR: LoadError: InitError: /home/isensee/.julia/packages/SpecialFunctions/fvheQ/deps/usr/lib/lib…

However, I think you can try installing in another computer, and then copy the ~/.julia directory to the offline computer. Sometimes it gives problem by differences in the systems, but it is the simpler way. I suggest you to test it.

---

<div class="post-metadata">

**Author:** ![gdestouet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdestouet/32/6175_2.png) [@gdestouet](https://discourse.julialang.org/u/gdestouet)\
**Post date:** [January 25, 2019, 12:26pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/3 "2019-01-25T12:26:51Z")

</div>

Well with this solution the problem is that I can only use packages built on a windows OS, and since the offline computer is using debian I think it is going to be tricky .

I might get around that by using `Pkg.develop` and configuring myself the dependency like I would do for a private package.

It is going to be cumbersome but I guess I don’t have another choice.

---

<div class="post-metadata">

**Author:** ![fredrikekre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fredrikekre/32/1688_2.png) [@fredrikekre](https://discourse.julialang.org/u/fredrikekre)\
**Post date:** [January 25, 2019, 12:51pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/4 "2019-01-25T12:51:48Z")

</div>

Use a docker container.

---

<div class="post-metadata">

**Author:** ![johnh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/johnh/32/3615_2.png) [@johnh](https://discourse.julialang.org/u/johnh)\
**Post date:** [January 25, 2019, 1:13pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/5 "2019-01-25T13:13:19Z")

</div>

@gdestouet You might look at Julia Team [https://juliacomputing.com/products/juliateam.html](https://juliacomputing.com/products/juliateam.html)

---

<div class="post-metadata">

**Author:** ![gdestouet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdestouet/32/6175_2.png) [@gdestouet](https://discourse.julialang.org/u/gdestouet)\
**Post date:** [January 25, 2019, 1:15pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/6 "2019-01-25T13:15:55Z")

</div>

Thank you for your response.  
So if ever want to add or upgrade a package, I will have to build a docker image and reinstall it ? It seems a bit cumbersome at the beginning but I will give it a try.

---

<div class="post-metadata">

**Author:** ![johnh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/johnh/32/3615_2.png) [@johnh](https://discourse.julialang.org/u/johnh)\
**Post date:** [January 25, 2019, 1:16pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/7 "2019-01-25T13:16:02Z")

</div>

> [@gdestouet](#):
>
> For each packages, download its github repo and store it somewhere at `local/path/pkg_n`

Did you do this using ‘git clone’ ? Please give us an example of the package you wish to install and the command you used to create the clone. Thanks!

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 25, 2019, 1:25pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/8 "2019-01-25T13:25:44Z")

</div>

You can automate it. Also, offline environments are already cumbersome, the best you can do is to come up with a workflow that mitigates this to some extent.

---

<div class="post-metadata">

**Author:** ![gdestouet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdestouet/32/6175_2.png) [@gdestouet](https://discourse.julialang.org/u/gdestouet)\
**Post date:** [January 25, 2019, 1:26pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/9 "2019-01-25T13:26:21Z")

</div>

For a simple test I tried with [Unitful.jl](https://github.com/ajkeller34/Unitful.jl), I wasn’t using `git clone` for this but only the decompressed archive.

With git clone it works well when using `Pkg.add`, I am going to do this for any packages I need.

I will do this with a package with many dependencies and come back with what I did.

---

<div class="post-metadata">

**Author:** ![gdestouet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdestouet/32/6175_2.png) [@gdestouet](https://discourse.julialang.org/u/gdestouet)\
**Post date:** [January 30, 2019, 4:28pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/10 "2019-01-30T16:28:33Z")

</div>

Unfortunately I can’t use Docker Images and I would prefer to build packages on the offline machine, so here is what I did :

Let say that I have a project on a computer connected to internet that I want to export and install on an offline computer along with its dependencies ( outside Julia’s stdlib ).

For this project I have the canonical structure :

```julia
path/to/Project_test
│ Manifest.toml
│ Project.toml
└───src/
        Project_Test.jl
        *.jl

```

I make sure that all dependencies of `Project_Test` are installed and present in `Manifest.toml` and that it is running properly. And that both computers have the same project with the same dependencies.

Instead of copying all the packages from the default depot path (`.julia/packages/...`) to julia depot path on the offline computer. The following function ( a flattened and pruned version of `Pkg.Types.add_or_develop` and `Pkg.Operates.add_or_develop` combined ) will download all the packages needed and store it on the external\_device at `path_to_external` . It will keep the structure used in the default depot path ( version-dependent ).It will also copy the registry to avoid `Pkg.Types.manifest_info` to call for a remote clone of the default registry while installing packages on the offline computer.

```julia
import Pkg.Types: PackageSpec,GitRepo,Context,project_deps_resolve!,registry_resolve!,stdlib_resolve!,ensure_resolved,manifest_info,write_env,update_registries,pkgerror,printpkgstyle
import Pkg.activate
import Pkg.Operations:resolve_versions!,version_data!,install_archive
import Pkg.BinaryProvider
using UUIDs
import LibGit2

function export_packages(path_to_project,path_to_external)
    activate(path_to_project)
    ctx=Context()
    project = ctx.env.project

    pkgs = [PackageSpec(k,v) for (k,v) in project.deps]

    project_deps_resolve!(ctx.env,pkgs)
    registry_resolve!(ctx.env,pkgs)
    stdlib_resolve!(ctx,pkgs)
    ensure_resolved(ctx.env,pkgs)
    for pkg in pkgs
        ctx.env.project.deps[pkg.name] = pkg.uuid
    end

    for (name::String, uuid::UUID) in ctx.env.project.deps
        entry = manifest_info(ctx.env, uuid)
        entry !== nothing && entry.version !== nothing || continue
        version = VersionNumber(entry.version)
        for pkg in pkgs
            pkg.uuid == uuid && version ∈ pkg.version || continue
            pkg.version = version
        end
    end

    resolve_versions!(ctx, pkgs)
    hashes, urls = version_data!(ctx, pkgs)

    for pkg in pkgs
        pkg.uuid in keys(ctx.stdlibs) && continue
        pkg.repo = GitRepo(urls[pkg.uuid][1],nothing,nothing)
    end

    BinaryProvider.probe_platform_engines!()
    pkgs_to_install = Tuple{PackageSpec, String}[]
    for pkg in pkgs
        pkg.uuid in keys(ctx.stdlibs) && continue
        path = joinpath(path_to_external,pkg.name,Base.version_slug(pkg.uuid,hashes[pkg.uuid]))
        push!(pkgs_to_install, (pkg, path))
    end

    widths = [textwidth(pkg.name) for (pkg, _) in pkgs_to_install]
    max_name = length(widths) == 0 ? 0 : maximum(widths)

    results = Channel(length(pkgs));
    i=1
    for (pkg, path) in pkgs_to_install
        print("$i over $(length(pkgs_to_install)) downloaded packages\r")
        try
            success = install_archive(urls[pkg.uuid], hashes[pkg.uuid], path)
            if ctx.use_only_tarballs_for_downloads && !success
                pkgerror("failed to get tarball from $(urls[pkg.uuid])")
            end
            put!(results, (pkg, success, path))
        catch err
            put!(results, (pkg, err, catch_backtrace()))
        end
        i+=1
    end

    missed_packages = Tuple{PackageSpec, String}[]
    for i in 1:length(pkgs_to_install)
        pkg, exc_or_success, bt_or_path = take!(results)
        exc_or_success isa Exception && pkgerror("Error when installing package $(pkg.name):\n",
                                                 sprint(Base.showerror, exc_or_success, bt_or_path))
        success, path = exc_or_success, bt_or_path
        if success
            vstr = pkg.version != nothing ? "v$(pkg.version)" : "[$h]"
            printpkgstyle(ctx, :Installed, string(rpad(pkg.name * " ", max_name + 2, "─"), " ", vstr))
        else
            push!(missed_packages, (pkg, path))
        end
    end

    for (pkg, path) in missed_packages
        uuid = pkg.uuid
        if !ctx.preview
            install_git(ctx, pkg.uuid, pkg.name, hashes[uuid], urls[uuid], pkg.version::VersionNumber, path)
        end
        vstr = pkg.version != nothing ? "v$(pkg.version)" : "[$h]"
        @info "Installed $(rpad(pkg.name * " ", max_name + 2, "─")) $vstr"
    end

end

```

Once the packages are stored on the external device. I used the following script to install and build them on the offline computer.

```julia
import Pkg:activate,depots1
import Pkg.Types:Context
import Pkg.Operations:build_versions

function add_local_packagesv11(pkgs_path,project_path)
    activate(project_path)
    ctx=Context()
    uuids_to_build=collect(keys(ctx.env.manifest))
    files=Set(readdir(pkgs_path))
    registry_path = joinpath(depots1(),"registries")
    Base.cp(joinpath(pkgs_path,pop!(files,"registries")),registry_path,force=true)
    default_path=joinpath(depots1(),"packages")
    for file in files
        path=joinpath(pkgs_path,file)
        if isdir(path)
            Base.cp(path,joinpath(default_path,file),force=true)
        end
    end
    build_versions(ctx,uuids_to_build)
end

project_path = "/path/to/Project_Test"
pkgs_path= "/path/to/external_device/pkgs"
add_local_packagesv11(pkgs_path,project_path)

```

Works well if the packages used doesn’t require internet to build, in which case you will have to configure the `deps/build.jl` scripts.  
For example, if I want to build `DSP` on the offline computer I will have to modify its `build.jl` and download the specific FFTW library it needs.  
Whereas, `Conda` builds well offline.

Tested with a project with ~40 dependencies on Julia 1.1.0.

---

<div class="post-metadata">

**Author:** ![tom\_tarter](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tom_tarter/32/7978_2.png) [@tom\_tarter](https://discourse.julialang.org/u/tom_tarter)\
**Post date:** [May 3, 2019, 5:11pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/11 "2019-05-03T17:11:46Z")

</div>

I have created a Manifest.toml file and a Project.toml file using

pkg\> add SQLite JLD Printf

and then I was able to use your export\_packages function to create a directory of all the files and dependencies. Unfortunately the function add\_local\_packagesv11 errors on the line

```
Base.cp(joinpath(pkgs_path,pop!(files,"registries")),registry_path,force=true)

```

I never created a registries file and am not sure how. Can you provide some help on what i’m missing?

Thanks,

---

<div class="post-metadata">

**Author:** ![gdestouet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdestouet/32/6175_2.png) [@gdestouet](https://discourse.julialang.org/u/gdestouet)\
**Post date:** [May 4, 2019, 10:23pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/12 "2019-05-04T22:23:24Z")

</div>

I might have done some modifications. I will put here the two scripts I use :

The first one for exporting a zip file containing all the packages useful for a given project

```julia
import Pkg.Types: PackageSpec,GitRepo,Context,project_deps_resolve!,registry_resolve!,stdlib_resolve!,ensure_resolved,manifest_info,write_env,update_registries,pkgerror,printpkgstyle
import Pkg.activate
import Pkg.Operations:resolve_versions!,version_data!,install_archive
import Pkg.BinaryProvider
import Pkg:depots1
using UUIDs
import LibGit2

function main(path_to_project,path_to_external)
    !isdir(path_to_external) ? mkdir(path_to_external) : nothing
    activate(path_to_project)
    ctx=Context()
    println("Project Path : " * path_to_project)
    println("Using : " * ctx.env.manifest_file * "\n"
           *" " * ctx.env.project_file)
    println("Copying these files to " * path_to_external)
    Base.cp(ctx.env.project_file,joinpath(path_to_external,basename(ctx.env.project_file)),force=true)
    Base.cp(ctx.env.manifest_file,joinpath(path_to_external,basename(ctx.env.manifest_file)),force=true)
    project = ctx.env.project
    registry_path = joinpath(depots1(),"registries")
    registry_path_extern = joinpath(path_to_external,"registries")
    mkpath(registry_path_extern)
    Base.cp(registry_path,registry_path_extern,force=true)

    pkgs = [PackageSpec(k,v) for (k,v) in project.deps]

    project_deps_resolve!(ctx.env,pkgs)
    registry_resolve!(ctx.env,pkgs)
    stdlib_resolve!(ctx,pkgs)
    ensure_resolved(ctx.env,pkgs)
    for pkg in pkgs
        ctx.env.project.deps[pkg.name] = pkg.uuid
    end

    for (name::String, uuid::UUID) in ctx.env.project.deps
        entry = manifest_info(ctx.env, uuid)
        entry !== nothing && entry.version !== nothing || continue
        version = VersionNumber(entry.version)
        for pkg in pkgs
            pkg.uuid == uuid && version ∈ pkg.version || continue
            pkg.version = version
        end
    end

    resolve_versions!(ctx, pkgs)
    hashes, urls = version_data!(ctx, pkgs)
    for pkg in pkgs
        pkg.uuid in keys(ctx.stdlibs) && continue
        pkg.repo = GitRepo(urls[pkg.uuid][1],nothing,nothing)
    end

    BinaryProvider.probe_platform_engines!()
    pkgs_to_install = Tuple{PackageSpec, String}[]
    for pkg in pkgs
        pkg.uuid in keys(ctx.stdlibs) && continue
        path = joinpath(path_to_external,pkg.name,Base.version_slug(pkg.uuid,hashes[pkg.uuid]))
		if !isdir(path)
			push!(pkgs_to_install, (pkg, path))
		end
    end

    widths = [textwidth(pkg.name) for (pkg, _) in pkgs_to_install]
    max_name = length(widths) == 0 ? 0 : maximum(widths)

    results = Channel(length(pkgs));
    i=1
    for (pkg, path) in pkgs_to_install
        print(" $i over $(length(pkgs_to_install)) downloaded packages\n
                    Currently processing $(pkg)\r")
        try
            success = install_archive(urls[pkg.uuid], hashes[pkg.uuid], path)
            if ctx.use_only_tarballs_for_downloads && !success
                pkgerror("failed to get tarball from $(urls[pkg.uuid])")
            end
            put!(results, (pkg, success, path))
        catch err
            put!(results, (pkg, err, catch_backtrace()))
        end
        i+=1
    end

    missed_packages = Tuple{PackageSpec, String}[]
    for i in 1:length(pkgs_to_install)
        pkg, exc_or_success, bt_or_path = take!(results)
        exc_or_success isa Exception && pkgerror("Error when installing package $(pkg.name):\n",
                                                 sprint(Base.showerror, exc_or_success, bt_or_path))
        success, path = exc_or_success, bt_or_path
        if success
            vstr = pkg.version != nothing ? "v$(pkg.version)" : "[$h]"
            printpkgstyle(ctx, :Installed, string(rpad(pkg.name * " ", max_name + 2, "─"), " ", vstr))
        else
            push!(missed_packages, (pkg, path))
        end
    end

    for (pkg, path) in missed_packages
        uuid = pkg.uuid
        if !ctx.preview
            install_git(ctx, pkg.uuid, pkg.name, hashes[uuid], urls[uuid], pkg.version::VersionNumber, path)
        end
        vstr = pkg.version != nothing ? "v$(pkg.version)" : "[$h]"
        @info "Installed $(rpad(pkg.name * " ", max_name + 2, "─")) $vstr"
    end

end

path_to_project = "your_project_path"
path_to_external = "where_to_export"
main(path_to_project,path_to_external)
## Optional, create a .zip with all the packages
zip_exe = "path_to_zip_exe"
zip_name = abspath(joinpath(path_to_external,"../julia_packages.zip"))
command = `cmd /c $(zip_exe) u -tzip $(zip_name) $(path_to_project) $(path_to_external)`
run(command)

```

Now that you have your .zip file or the folder with all the packages, you can put this on the offline computer and make the installation with the following script

```julia
import Pkg:activate,depots1
import Pkg.Types:Context
import Pkg.Operations:build_versions

function add_local_packagesv11(pkgs_path,project_path)
    activate(project_path)
    ctx=Context()
    uuids_to_build=collect(keys(ctx.env.manifest))
    files=Set(readdir(pkgs_path))
    pop!(files,"Manifest.toml")
    pop!(files,"Project.toml")
    registry_path = joinpath(depots1(),"registries")
    Base.cp(joinpath(pkgs_path,pop!(files,"registries")),registry_path,force=true)
    default_path=joinpath(depots1(),"packages")
    for file in files
        	path = joinpath(pkgs_path,file)
	        path_project = joinpath(default_path,file)
        	versions_pkgs = Set(readdir(path))
	        versions_project = isdir(path_project) ? Set(readdir(path_project)) : Set()
		for version in setdiff(versions_pkgs,versions_project)
			path_version = joinpath(path_project,version)
			path_pkg_version = joinpath(path,version)
			mkpath(path_version)
			Base.cp(path_pkg_version,path_version,force=true)
		end
    end
    build_versions(ctx,uuids_to_build)
end

pkgs_path = "where_the_exported_packages_are"
project_path = "path_to_project"
pkgs_path= joinpath(pkgs_path,project_path)
add_local_packagesv11(pkgs_path,project_path)

```

This script only install new packages or newer versions, if you want to override a package you can manually delete it or slightly modify this script with options.

Try with these scripts and let me know if you still have problems. Also, some packages need to be built with libraries, you will need to look in `build.jl` files for download links and put libraries under `pkg_being_built/deps/usr/downloads` ( or sometimes `pkg_being_built/deps/usr` ) and make sure that during build the `build.jl` doesnt delete the library you just manually installed ( you can pass a `cat build.jl | grep rm` to check this ).

Hope this help.

---

<div class="post-metadata">

**Author:** ![Bisounours](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bisounours/32/10600_2.png) [@Bisounours](https://discourse.julialang.org/u/Bisounours)\
**Post date:** [September 18, 2019, 11:42am UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/14 "2019-09-18T11:42:23Z")

</div>

Hello !

A repo on github is available here : [JuliaOffline](https://github.com/hgixp4y0hk56c/JuliaOffline)  
Currently, three scripts are available :

1. For downloading packages source files
2. One that parses build.jl files generated by BinaryProvider and download the right binaries according to the architecture of the offline computer
3. the last one automatically installs packages from step 1 and builds the acquired binaries from step 2 on the offline computer

It stills needs some testing and improvements so feel free to propose your own code.

---

<div class="post-metadata">

**Author:** ![sdewaele](https://avatars.discourse-cdn.com/v4/letter/s/7ab992/32.png) [@sdewaele](https://discourse.julialang.org/u/sdewaele)\
**Post date:** [October 2, 2019, 1:50pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/15 "2019-10-02T13:50:01Z")

</div>

Thanks for sharing this code, this is very useful! I am in the situation where I don’t have internet access on the system that generates the depot. Building on your code I wrote a function to create a depot for the packages in a given environment. This allows you to derive a small depot for a specific application from a larger depot. It also has the benefit to include binary dependencies.

```julia

using Pkg.Types: Context,project_deps_resolve!,registry_resolve!,stdlib_resolve!,ensure_resolved,manifest_info,write_env,update_registries,pkgerror,printpkgstyle
using Pkg
using Pkg.TOML
using Pkg.Operations:resolve_versions!,version_data!
using UUIDs

"""
    copypackages(ctx,pkgs,dest_depot_path) -> allpkgs

Copy packages `pkgs` to `dest_depot_path`. Returns `allpkgs`, which
contains the original packages and all it dependecies, as copied.
"""
function copypackages(ctx,pkgs,dest_depot_path)
  pkgs = copy(pkgs)

  # Resolve dependencies and version
  project_deps_resolve!(ctx.env,pkgs)
  registry_resolve!(ctx.env,pkgs)
  stdlib_resolve!(ctx,pkgs)
  ensure_resolved(ctx.env,pkgs)
  resolve_versions!(ctx, pkgs)

  # Copy to destination
  max_name = maximum(textwidth(pkg.name) for pkg in pkgs)
  src_package_dir = joinpath(Pkg.depots1(),"packages")
  dest_package_dir = joinpath(dest_depot_path,"packages")
  hashes,_ = version_data!(ctx, pkgs)
  for pkg in pkgs
    pkg.uuid ∈ keys(ctx.stdlibs) && continue
    slug = Base.version_slug(pkg.uuid,hashes[pkg.uuid])
    srcdir = joinpath(Pkg.depots1(),"packages",pkg.name,slug)
    dstdir = joinpath(dest_depot_path,"packages",pkg.name,slug)
    mkpath(dstdir)
    cp(srcdir,dstdir,force=true)
    vstr = isnothing(pkg.version) ? "[-]" : "v$(pkg.version)" 
    printpkgstyle(ctx, :Packaged, string(rpad(pkg.name * " ", max_name + 2, "─"), " ", vstr))
  end
  pkgs
end

"""
    packagedepot(env_path,dest_depot_path;startup="",registry_repo_only=false) -> zipfile

Generate depot out of environment `env_path` and store in depot
directory `dest_depot_path`.
`startup` may contain a startup script. If `registry_repo_only` is `true`, only
store the registry depot `registries/General/.git`. This considerably accelerates the (un)zip,
but requires git to be present on the system where the depot is deployed. 
"""
function packagedepot(env_path,dest_depot_path;startup="",registry_repo_only=false)
  printstyled("Creating depot from environment\n$env_path\n",color=:blue)

  isdir(dest_depot_path) || mkpath(dest_depot_path)
  Pkg.activate(env_path)

  ctx = Context()

  # Packages
  pkgs = [PackageSpec(k,v) for (k,v) in ctx.env.project.deps]
  copypackages(ctx,pkgs,dest_depot_path)
  printstyled("✓ packages\n",color=:blue)

  # Project.toml and Manifest.toml
  v = VERSION
  env_dest_path = joinpath(dest_depot_path,"environments","v$(v.major).$(v.minor)")
  mkpath(env_dest_path)
  cp(ctx.env.project_file,joinpath(env_dest_path,basename(ctx.env.project_file)),force=true)
  cp(ctx.env.manifest_file,joinpath(env_dest_path,basename(ctx.env.manifest_file)),force=true)
  printstyled("✓ Project.toml/Manifest.toml\n",color=:blue)

  # Registry
  registry_path = joinpath(Pkg.depots1(),"registries")
  dest_registry_path = joinpath(dest_depot_path,"registries")
  if registry_repo_only
    registry_path,dest_registry_path = joinpath.([registry_path,dest_registry_path],"General",".git")
  end
  mkpath(dest_registry_path)
  cp(registry_path,dest_registry_path,force=true)
  msg = "✓ registry"
  registry_repo_only && (msg *= " - git repo only")
  printstyled(msg*"\n",color=:blue)

  # Add config/startup.jl
  if !isempty(startup)
    configdir = joinpath(dest_depot_path,"config")
    mkpath(configdir)
    write(joinpath(configdir,"startup.jl"),startup)
    printstyled("✓ startup.jl\n",color=:blue)
  end

  # Archive to zip file
  zipfile_fullpath = dest_depot_path*".zip"
  rm(zipfile_fullpath,force=true)
  zipfile = basename(zipfile_fullpath)
  command = `zip -r -q -o $zipfile .`
  cd(dest_depot_path) do   
    run(command)
    mv(zipfile,joinpath("..",zipfile))
  end
  # @time run(pipeline(command,stdout=Pipe()))
  printstyled("✓ zip file\n$zipfile_fullpath\n",color=:blue)

  zipfile
end

```

```julia

```

---

<div class="post-metadata">

**Author:** ![wulpuqu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wulpuqu/32/17640_2.png) [@wulpuqu](https://discourse.julialang.org/u/wulpuqu)\
**Post date:** [October 15, 2020, 1:12pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/16 "2020-10-15T13:12:01Z")

</div>

With the introduction of Artifacts in Pkg for Julia v1.3+, this simple script does the work, by calling julia with `julia --project=/path/to/project`

```julia
using Pkg
# On the host machine, call the following line to get the right 'machine' string corresponding to the host (here a Debian linux machine)
#machine=string(Sys.MACHINE,BinaryPlatforms.compiler_abi_str(BinaryPlatforms.detect_compiler_abi(platform_key_abi(Sys.MACHINE))))

machine="x86_64-pc-linux-gnu-libgfortran4-cxx11" # See above
platform = Pkg.BinaryPlatforms.platform_key_abi(machine)

path_to_project = # Fill this
path_to_external = # Fill this
ispath(path_to_external) || mkpath(path_to_external)
empty!(DEPOT_PATH)
push!(DEPOT_PATH,path_to_external)

function main(path_to_project,path_to_external,platform)
    Pkg.activate(path_to_project)
    ctx=Pkg.Types.Context()
    println("Project Path : " * path_to_project)
    println("Using : " * ctx.env.manifest_file * "\n"
           *" " * ctx.env.project_file)
    println("Installing to " * path_to_external)
    println("Platform $platform")
    Pkg.instantiate(ctx;platform=platform,verbose=true)
end

main(path_to_project,path_to_external,platform)

```

It creates a new depot at `path_to_external` which can then be exported on the host machine.  
On the host machine, you have to run `JULIA_DEPOT_PATH=/path/to/exported/depot julia` and launch `using Pkg; Pkg.build()`.

You might have some problems with not found libraries, but don’t worry it is generally just a symlink/renaming problem.

Note that if your project use `Conda/PyCall`, it won’t install a proper python distribution corresponding to your machine, you have to install it manually and specify the `CONDA_JL_HOME` environment variable.

For `Plots.jl` with GR (unfortunately not using `Pkg.Artifacts` for the moment) you have to directly download the library at `https://github.com/sciapp/gr/releases`, install it in` GR/###/deps/downloads` and remove this line in `GR/###/deps/build.jl`  
[https://github.com/jheinen/GR.jl/blob/b275fcf52800018fd272ec9baa0259759f9b9801/deps/build.jl#L134](https://github.com/jheinen/GR.jl/blob/b275fcf52800018fd272ec9baa0259759f9b9801/deps/build.jl#L134)

---

<div class="post-metadata">

**Author:** ![psteffensen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/psteffensen/32/37583_2.png) [@psteffensen](https://discourse.julialang.org/u/psteffensen)\
**Post date:** [June 2, 2021, 9:11pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/17 "2021-06-02T21:11:44Z")

</div>

I used your script succesfully and moved the extracted files from the computer with internet access to the offline computer.

But when I tried to build the packages on the offline computer it still wants to check online:

```julia
julia> using Pkg; Pkg.build()
   Updating registry at `C:\Julia_Depot\registries\General`
┌ Warning: could not download https://pkg.julialang.org/registries
└ @ Pkg.Types C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Pkg\src\Types.jl:951
No Changes to `C:\Julia_Depot\environments\v1.5\Project.toml`
No Changes to `C:\Julia_Depot\environments\v1.5\Manifest.toml`

```

I don’t know how to go further. Do you have a suggestion to what I could try?

* * *

I think it works now. Thank you wulpuqu and ericphanson! I was just extracting the wrong library. Also the Pkg.offline() (JULIA\_PKG\_OFFLINE set to true in envionment variables) helped a lot, so I was not looking for the wrong problem.

One thing I need to do, was to copy Manifest.toml and Project.toml from the source library to environments/1.5/ (in “\<user\_folder\>/.julia”).

---

<div class="post-metadata">

**Author:** ![ericphanson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ericphanson/32/215186_2.png) [@ericphanson](https://discourse.julialang.org/u/ericphanson)\
**Post date:** [June 2, 2021, 11:19pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/18 "2021-06-02T23:19:54Z")

</div>

You can use `Pkg.offline()`; I can’t find it in the Pkg manual, but you can see the code and docstring here: [https://github.com/JuliaLang/Pkg.jl/blob/2029bee7e94140aa0614f7ec4077042c419a2fe9/src/Pkg.jl#L441](https://github.com/JuliaLang/Pkg.jl/blob/2029bee7e94140aa0614f7ec4077042c419a2fe9/src/Pkg.jl#L441)

---

<div class="post-metadata">

**Author:** ![manuelbb-upb](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/manuelbb-upb/32/25222_2.png) [@manuelbb-upb](https://discourse.julialang.org/u/manuelbb-upb)\
**Post date:** [June 10, 2021, 8:53am UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/19 "2021-06-10T08:53:50Z")

</div>

Thanks for your script! It’s exactly what i have been looking for.

It seems there have been some changes to `BinaryPlatforms` though.  
On the host machine do the following

```julia
julia -e 'println(Base.BinaryPlatforms.triplet(Base.BinaryPlatforms.HostPlatform()))'

```

and copy the platform string that is returned.

The script from above then becomes

```julia
using Pkg

# copy platform string of the target machine from command above
platform_str = "x86_64-linux-gnu-libgfortran4-cxx11-libstdcxx26-julia_version+1.6.1"
platform = Base.BinaryPlatforms.parse( Base.BinaryPlatforms.Platform, platform_str )

path_to_project = # Fill this
path_to_external = # Fill this
ispath(path_to_external) || mkpath(path_to_external)
empty!(DEPOT_PATH)
push!(DEPOT_PATH,path_to_external)

function main(path_to_project,path_to_external,platform)
    Pkg.activate(path_to_project)
    ctx=Pkg.Types.Context()
    println("Project Path : " * path_to_project)
    println("Using : " * ctx.env.manifest_file * "\n"
           *" " * ctx.env.project_file)
    println("Installing to " * path_to_external)
    println("Platform $platform")
    Pkg.instantiate(ctx;platform=platform,verbose=true)
end

main(path_to_project,path_to_external,platform)

```

_Edit_: In the above script, you can also use this function

```julia
function add_package(name)
    Pkg.activate(tempname())
    ctx=Pkg.Types.Context()
    parsed_name = Pkg.REPLMode.parse_package_identifier(name; add_or_develop = true)
    Pkg.add(ctx, [parsed_name,]; platform=platform)
end

```

to download a package and its dependencies on the host machine into the DEPOT\_PATH.  
It works with git urls, too.

---

<div class="post-metadata">

**Author:** ![kschertler](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kschertler/32/12899_2.png) [@kschertler](https://discourse.julialang.org/u/kschertler)\
**Post date:** [June 11, 2021, 10:05am UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/20 "2021-06-11T10:05:24Z")

</div>

> [@wulpuqu](#):
>
> With the introduction of Artifacts in Pkg for Julia v1.3+, this simple script does the work, by calling julia with `julia --project=/path/to/project`

Is there any particular reason to activate the environment already from the command line with ˋjulia --project …ˋ, or wouldn’t a later ˋPkg\> activate …ˋ serve the same purpose?

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [June 13, 2021, 5:28pm UTC](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083/21 "2021-06-13T17:28:54Z")

</div>

They serve the same purpose. The choice is entirely up to what you find most convenient. For my part I usually have the current directory of the shell at the project when I work with something and then activating it is just `julia --project`. If I restart Julia I just need to do arrow up and return in the shell to get the project activated again.

[Next page](https://discourse.julialang.org/t/offline-installation-of-julia-packages/20083.md?page=2)
