How to Install R Packages in Linux with devtools::install_github()

I recently ran into a frustrating issue while trying to install the leaflet R package directly from GitHub on my Linux server. The CRAN version was outdated, so I wanted the latest and greatest. Here’s the story of how it went wrong, what the error actually meant, and how I fixed it.

The Problem I Faced

I wrote a simple R script that looked like this:

library(httr)
library(devtools)

set_config(use_proxy(url = "xxx", port = xxxx))
devtools::install_github("rstudio/leaflet")

I expected it to grab the GitHub repo and build. Instead, R threw this error:

Downloading GitHub repo rstudio/leaflet@master
from URL https://api.github.com/repos/rstudio/leaflet/zipball/master
Error in curl::curl_fetch_memory(url, handle = handle) :
  Couldn't connect to server
Calls: <Anonymous> ... request_fetch -> request_fetch.write_memory -> <Anonymous> -> .Call
Execution halted

Interestingly, installing packages from CRAN worked fine only GitHub failed.

What The Error Really Meant

After some digging, I learned that:

  • httr::set_config(use_proxy()) doesn’t apply here.
    devtools actually relies on the remotes package, which uses libcurl under the hood. Libcurl doesn’t care about httr’s proxy settings it looks for environment variables (http_proxy, https_proxy, etc.).
  • Corporate networks add an extra wrinkle.
    My company uses a proxy that intercepts HTTPS traffic and re-signs certificates. That means libcurl sometimes doesn’t trust GitHub’s SSL cert unless I provide the corporate CA.
  • Other possible blockers:
    • Firewall not letting me reach api.github.com or codeload.github.com
    • GitHub API rate limits (which I can fix with a personal access token)

The Fix That Work

I stopped fighting httr configs and went straight to environment variables. Here’s the working setup:

# One-off for this R session
Sys.setenv(
  http_proxy  = "http://user:pass@proxy.myco.local:8080",
  https_proxy = "http://user:pass@proxy.myco.local:8080",
  no_proxy    = "localhost,127.0.0.1,.myco.local"
)

# Use libcurl explicitly
options(download.file.method = "libcurl")

# Add a GitHub token (optional, avoids rate limiting)
Sys.setenv(GITHUB_PAT = "<your_personal_access_token>")

# Try again
remotes::install_github("rstudio/leaflet")

After that, the installation worked.

Step by Step Diagnostics I Use

  1. Check if GitHub is reachable (from Linux shell):
curl -I https://api.github.com
curl -I https://codeload.github.com

If that failed, but worked with -x http://proxy:8080, I knew the proxy was required.

  1. Check inside R:
curl::curl_version()$ssl_version
Sys.getenv(c("http_proxy","https_proxy","no_proxy","CURL_CA_BUNDLE","SSL_CERT_FILE"))

If those variables were empty, I knew libcurl wasn’t seeing my proxy.

  1. Test a manual download in R:
download.file(
  "https://api.github.com/rate_limit",
  tempfile(fileext = ".json"),
  method = "libcurl"
)

If that failed, it confirmed proxy/SSL problems.

Configuring the Proxy Permanently

To avoid setting it every time, I saved my proxy in ~/.Renviron:

usethis::edit_r_environ()

And added:

http_proxy=http://user:pass@proxy.myco.local:8080
https_proxy=http://user:pass@proxy.myco.local:8080
no_proxy=localhost,127.0.0.1,.myco.local
GITHUB_PAT=ghp_XXXXXXXXXXXXXXXXX

After restarting R, everything worked without extra code.

Some Practice Functions I Built

To make life easier, I wrote a helper:

install_from_github <- function(repo, proxy = NULL, pat = Sys.getenv("GITHUB_PAT", "")) {
  if (!is.null(proxy)) {
    Sys.setenv(http_proxy = proxy, https_proxy = proxy)
  }
  if (nzchar(pat)) Sys.setenv(GITHUB_PAT = pat)
  options(download.file.method = "libcurl")
  remotes::install_github(repo)
}

Now I can just call:

install_from_github("rstudio/leaflet", proxy = "http://user:pass@proxy:8080")

I also added a quick check:

check_github_connectivity <- function() {
  hosts <- c("https://api.github.com", "https://codeload.github.com")
  sapply(hosts, function(h) {
    tryCatch({ curl::curl_fetch_memory(h); "OK" },
             error = function(e) conditionMessage(e))
  })
}

This way I can see instantly if GitHub is reachable.

Why CRAN Work

This one surprised me at first. CRAN mirrors were reachable without the proxy (probably because my network allows them). GitHub wasn’t. Plus, some CRAN installs used wget (which already knew about my proxy), while devtools used libcurl directly and libcurl had no idea about my proxy until I set those environment variables.

Final Thoughts

In the end, the error message curl_fetch_memory: Couldn't connect to server wasn’t about R or devtools being broken. It was simply about my Linux server not knowing how to reach GitHub through the proxy. Once I understood that httr’s proxy settings weren’t enough, everything clicked.

Related blog posts