Making R portable on macOS
The companion Windows post covers why portable R matters and where it fits in the broader landscape of runtime distribution. On Windows, R has been relocatable since the R 2.x era, so the work there was mostly removing the installer from the equation. On macOS, the engineering goes deeper.
Community interest in making R portable on macOS goes back over a decade (Figure 1). A 2009 R-devel proposal suggested making bin/R infer its location at runtime. A 2013 discussion explored bundling R in an IDE, with Simon Urbanek mapping out the landscape of paths that get baked in at build time. A 2015 SuperUser question and a 2019 Posit Community thread each contributed techniques and deepened understanding. And projects like COVAIL’s Electron template, r-shiny-electron, shiny-electron-template-m1, and selkamand’s r-portable-mac each proved that portable R on macOS is achievable and in demand.
portable-r-macos builds on all of this. The build script takes the official CRAN binary, rewrites every hardcoded framework path, codesigns with a Developer ID certificate, notarizes with Apple, and transparently patches CRAN binary packages at install time.
Getting started
Download a tar.gz from GitHub Releases and extract it. R.home() resolves to wherever you put the folder, packages install locally, and the Developer ID signature keeps Gatekeeper quiet (Figure 2):
Pick the archive for your Mac’s architecture (replace 4.5.3 with any version from 4.3.0 onward) and pipe it through tar. Releases for all versions are at github.com/portable-r/portable-r-macos/releases.
Apple Silicon (M1/M2/M3/M4)
curl -fSL https://github.com/portable-r/portable-r-macos/releases/download/v4.5.3/portable-r-4.5.3-macos-arm64.tar.gz | tar xzIntel
curl -fSL https://github.com/portable-r/portable-r-macos/releases/download/v4.5.3/portable-r-4.5.3-macos-x86_64.tar.gz | tar xzOnce extracted, R.home() points to wherever the folder landed. Installing packages works normally, and the .portable environment patches CRAN binary .so files automatically:
~/Downloads $ ./portable-r-4.5.3-macos-arm64/bin/Rscript -e 'cat(R.home(), "\n")'
# /Users/you/Downloads/portable-r-4.5.3-macos-arm64
~/Downloads $ ./portable-r-4.5.3-macos-arm64/bin/Rscript -e '
install.packages("jsonlite")
library(jsonlite)
cat(toJSON(list(portable = TRUE), auto_unbox = TRUE))
'
# Portable R: patched 1 shared library
# {"portable":true}No sudo, no .pkg, no Gatekeeper warnings. Packages install to a local library/ directory inside the portable R folder.
Patching for portability
The CRAN macOS .pkg embeds absolute /Library/Frameworks/R.framework/... paths in Mach-O binaries, shell scripts, and config files. The build script rewrites all of these to relative references (Figure 3).
@executable_path references.
This is the same Mach-O relocation approach used across the macOS ecosystem: Homebrew’s keg relocation (via ruby-macho), conda-build’s macOS relocator, and python-build-standalone’s build script all rewrite dylib load commands to @rpath and @loader_path references in the same way. The build also patches shell scripts and Makeconf with sed, wraps Rscript to set the RHOME environment variable it reads at startup, and codesigns everything with a Developer ID certificate and Apple notarization.
CRAN binary packages carry the same embedded framework paths in their .so files. A .portable R environment attaches to the search path at startup and wraps install.packages() to automatically patch these after installation:
> install.packages(c("jsonlite", "curl", "httr2"))
Portable R: patched 11 shared librariesFull technical details are in build-details.md.
Looking ahead
With both Windows and macOS covered, portable R is available for every major desktop operating system, across 12 R versions and multiple architectures, from a predictable URL pattern (Figure 4).
The next step is tooling that builds on this: a Shiny app packaged as a native executable, a deployment script that provisions R on the fly, an Electron or Tauri wrapper that downloads the right portable R on first launch. Posit’s portable Linux binary packages are bringing the same portability thinking to the package level. Together, these efforts move the R ecosystem toward what Python already has with python-build-standalone and PyInstaller: runtimes and packages that can be provisioned and shipped without assuming anything about the host system.
Fin
portable-r-macos is open source and available now at github.com/portable-r/portable-r-macos, with releases for R 4.3.0 through 4.5.3 across arm64 and x86_64, Developer ID signed and Apple notarized. The Windows counterpart is at github.com/portable-r/portable-r-windows and covered in the companion post.
References
- portable-r-macos: Source code and releases for this project.
- portable-r-windows: The Windows counterpart.
- R-devel, 2009: PR#14007: Proposal for relocatable R with runtime path detection.
- R-devel, 2013: Rscript path discussion: Exploration of baked-in paths when bundling R in an IDE.
- Simon Urbanek on relocatable R (2013): CRAN macOS maintainer maps out the landscape of baked-in paths.
- SuperUser: Self-contained R in OS X (2015): Exploration of portable R on macOS.
- Posit Community: Is R on Mac portable? (2019): Community investigation into macOS R portability.
- r-portable-mac (selkamand): Portable R for macOS at R 4.2.3.
- COVAIL electron-quick-start: Electron + bundled R for desktop Shiny apps.
- r-shiny-electron: Electron template for standalone Shiny applications.
- shiny-electron-template-m1: Apple Silicon fork of the Shiny Electron template.
- Posit: Portable Linux R Binary Packages (2025): Posit’s manylinux-style portable packages for Linux.
- Homebrew Bottles: Homebrew’s documentation on pre-built relocatable packages.
- Homebrew keg_relocate.rb: Homebrew’s macOS relocation logic using
@loader_path. - Homebrew ruby-macho: Pure-Ruby Mach-O manipulation library.
- conda-build: Making packages relocatable: conda-build documentation on macOS and Linux relocatability.
- conda-build macho.py: conda-build’s Python wrapper around
install_name_tool. - python-build-standalone build-cpython.sh: Astral’s build script with
install_name_toolcalls for relocatable Python. - python-build-standalone: Pre-built, relocatable Python distributions by the Astral team.
- PyInstaller: Bundles Python applications into standalone executables.
- briefcase: BeeWare project. Packages Python apps for macOS, Windows, Linux, iOS, and Android.
- Nuitka: Python compiler that produces standalone executables.