Installing Google Chrome On CentOS, Amazon Linux, or RHEL

By Evan Sangaline | September 6, 2017

The easiest way to install the latest Chrome version on RHEL, CentOS, and Amazon Linux versions 6.X and 7.X.

# This installs Chrome on any RHEL/CentOS/Amazon Linux variant.
curl https://intoli.com/install-google-chrome.sh | bash

A Universal Installation Script for Google Chrome on Amazon Linux and CentOS 6

CentOS, Amazon Linux AMI, and Red Hat Enterprise Linux are three closely related GNU/Linux distributions which are all popular choices for server installations. They offer excellent performance and stability, but package availability can often be lacking. The Extra Packages for Enterprise Linux (EPEL), a community maintained repository of additional packages, significantly improves the situation, but doesn’t include Google Chrome/Chromium or a lot of other software that you would expect on more desktop-oriented distributions. This can be a bit frustrating when you’re interested in running headless Chrome instances in the cloud for testing or web scraping.

Google does maintain a repository with a Chrome RPM package, but it only works with RHEL/CentOS 7.X versions. Amazon Linux is currently only available as a 6.X version, and the 6.X versions of RHEL/CentOS remain fairly common (in part due to the transition from upstart to systemd in 7.X). If you’re using one of these versions then you’re kind of on your own… and the installation process is quite complicated due to the lack of GTK 3 on RHEL 6.X.

We use headless Chrome instances for web scraping on Amazon Linux here at Intoli and so we maintain our own package which bundles the otherwise missing dependencies. In this article, we’ll show you both how to get up and running as quickly as possible with our modified Chrome RPM and explain the process of modifying RPMs to include additional libraries. If you’ve been searching around for how to run Chrome on Amazon Linux then hopefully you’ll find this useful!

The Easy Way

Universal Installation Script for RHEL/CentOS 6.X/7.X

All you need to do is run our installer script and you should be good to go.

curl https://intoli.com/install-google-chrome.sh | bash

This will automatically configure and enable the official Google repository, import Google’s signing key, and install the latest google-chrome-stable executable in your path. If you’re on a RHEL 6.X flavor the it will also automatically find and install all of the unmet dependencies that would normally make the installation fail. You can rerun the script whenever you want to grab the latest version of Google Chrome and, if you’re using RHEL 7.X, then you can update the package using yum as you would with any other package.

RHEL/CentOS 7.X

Alternatively, you can add the repository manually if you’re using a 7.X version. If you’re using Amazon Linux AMI then you’re definitely on a 6.X version and need to use the installation script. Otherwise, you can run

cat /etc/redhat-release

and check the output for the version number to confirm that you’re using 7.X. For example, if you’re using RHEL 7.0 then you would expect to see something like:

Red Hat Enterprise Linux Server release 7.0 (Maipo)

You can use the official Google repository if you see a 7.X version there. To add it to your system, simply create a file called /etc/yum.repos.d/google-chrome.repo with the following contents.

[google-chrome]
name=google-chrome
baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub

Then you just need to run

sudo yum install google-chrome-stable

to install the Chrome package. This will add the google-chrome-stable script in /usr/bin/. Note that this is the same thing that the installation script does on 7.X flavors, so it’s safe to use the script regardless of which version you’re on.

The Hard Way

If you just want to install Google Chrome on a RHEL variant then I highly recommend using our installation script in the previous section. That said, I’ll walk through the process of doing it manually because some of our more technical readers might find it interesting. The basic idea is to use the 7.X Google Chrome RPM as a starting point and then to install the dependencies and extras that we need in order for it to work on 6.X versions. This process is exactly the same as the one employed by the installation script above.

Getting the RPM

First, we’ll grab the official RPM file for the 7.X variants. It won’t work out of the box on 6.X versions, but it gives us a good starting point for making changes. We can download the file by creating the same /etc/yum.repos.d/google-chrome.repo file that we would use with the 7.X versions

[google-chrome]
name=google-chrome
baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub

and then running:

# install the yum utilities
sudo yum install yum-utils

# download the google-chrome-stable package
yumdownloader google-chrome-stable

The second command should download the RPM file to the current working directory. The current version has a file name of google-chrome-stable-63.0.3239.108-1.x86_64.rpm, but this will change as new versions get released.

Installing the (Broken) Package

Let’s start by installing as many of the dependencies of google-chrome-stable as we can. This can be done by using repoquery, another tool provided by yum-utils, to resolve the dependency packages and then passing these as arguments to yum using xargs.

repoquery --requires --resolve google-chrome-stable | xargs sudo yum -y install

After these finish installing, we can try to install the downloaded RPM.

sudo rpm -i google-chrome-stable-63.0.3239.108-1.x86_64.rpm

This will output a list of dependencies that aren’t available on the system.

error: Failed dependencies:
	libXss.so.1()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
	libatk-1.0.so.0()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
	libgconf-2.so.4()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
	libgdk-3.so.0()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
	libgdk_pixbuf-2.0.so.0()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
	libgtk-3.so.0()(64bit) is needed by google-chrome-stable-63.0.3239.108-1.x86_64
	xdg-utils is needed by google-chrome-stable-63.0.3239.108-1.x86_64

Let’s go ahead and install the package anyway, this time skipping the missing dependencies with the --nodeps option.

sudo rpm -i --nodeps google-chrome-stable-63.0.3239.108-1.x86_64.rpm

You’ll likely see an error like

Error: Could not find xdg-icon-resource
warning: %post(google-chrome-stable-63.0.3239.108-1.x86_64) scriptlet failed, exit status 1

because the RPM’s installation script includes the following code for installing the icons using xdg-icon-resource.

# Add icons to the system icons
XDG_ICON_RESOURCE="`which xdg-icon-resource 2> /dev/null || true`"
if [ ! -x "$XDG_ICON_RESOURCE" ]; then
  echo "Error: Could not find xdg-icon-resource" >&2
  exit 1
fi
for icon in "/opt/google/chrome/product_logo_"*.png; do
  size="${icon##*/product_logo_}"
  "$XDG_ICON_RESOURCE" install --size "${size%%.png}" "$icon" "google-chrome"
done

This isn’t anything to worry about, the installation most likely still worked even if you saw this error. If you run google-chrome-stable at this point, however, you’ll see an error like this.

/usr/bin/google-chrome-stable: error while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory

This one is more serious. Note that this libgconf-2.so.4 was one of the missing dependencies that was listed before we forced the installation of the RPM. It turns out that we actually do need those dependencies after all!

Providing the Missing Dependencies

Running the Chrome executable told us one missing library, but we can use ldd to get a more complete picture of the missing shared libraries that google-chrome-stable links to.

ldd /opt/google/chrome/chrome | grep "not found"

This command will output the list of missing libraries, all of which are ones that we removed as requirements.

	libgconf-2.so.4 => not found
	libXss.so.1 => not found
	libatk-1.0.so.0 => not found
	libgtk-3.so.0 => not found
	libgdk-3.so.0 => not found
	libgdk_pixbuf-2.0.so.0 => not found

We could try to build these libraries ourselves, or even to create new packages for them, but this would end up being an immense amount of work because each of these will have their own sets of dependencies. A much easier solution is to grab these files from another GNU/Linux system where the same version of Google Chrome is installed. This is a bit of a hack, but it’s the easiest way to get things working quickly.

There are two questions to answer here: where to get the libraries from and where to put them. Let’s start with where to put them because there’s an easy answer. One obvious choice would be /usr/lib64/, but we would eventually end up with some library files that are in conflict with existing system libraries. Plus it’s kind of gross to dump files that aren’t controlled by the package manager into those system directories. What are we, Mac users?

If we look inside of the /usr/bin/google-chrome-stable script, we can see that it actually prepends a few directories of its own to LD_LIBRARY_PATH before it launches Chrome.

# Always use our versions of ffmpeg libs.
# This also makes RPMs find the compatibly-named library symlinks.
if [[ -n "$LD_LIBRARY_PATH" ]]; then
  LD_LIBRARY_PATH="$HERE:$HERE/lib:$LD_LIBRARY_PATH"
else
  LD_LIBRARY_PATH="$HERE:$HERE/lib"
fi
export LD_LIBRARY_PATH

With the standard Chrome installation location, the extra library paths are /opt/google/chrome/ and /opt/google/chrome/lib/. These directories will be searched for dependencies before any of the system directories and they’re nicely colocated with the Chrome installation. The /opt/google/chrome/lib/ directory is actually non-existent in the package, but it’s a convenient location to place all of our library files.

Now we’re ready for the harder question: where do we get the libraries from? Well, given that the Chrome RPM can be installed successfully on CentOS 7, that seems like a good place to look. The same repoquery utility that we used earlier makes it possible to query remote repositories to find the RPMs that provide certain files. Let’s take the missing libgconf-2.so.4 for example.

# Search the CentOS 7 repository for the missing file.
repoquery --repofrompath=centos7,http://mirror.centos.org/centos/7/os/`arch` \
    --repoid=centos7 \ # Only consider this specific repository.
    --qf="%{location}" \ # Output the URL of the RPM file.
    --whatprovides libgconf-2.so.4 # Find the package that provides this library.

This command will output

http://mirror.centos.org/centos/7/os/x86_64/Packages/GConf2-3.2.6-8.el7.i686.rpm

which is so close, but not quite right. You can see from the /x86_64/ in the URL that the architecture for this repository for 64 bit, but the .i686.rpm indicates that this is the 32 bit package. We’ll need to manually change the suffix to .x86_64.rpm to specify the correct architecture for our CentOS 6 system (assuming that you’re using a 64 bit version).

After modifying the URL, we can download the RPM and extract it by running the following.

# Change to a temporary directory.
mkdir -p /tmp/working-directory/
cd /tmp/working-directory/

# Download the RPM.
wget http://mirror.centos.org/centos/7/os/x86_64/Packages/GConf2-3.2.6-8.el7.x86_64.rpm

# Extract it.
rpm2cpio GConf2-3.2.6-8.el7.x86_64.rpm | cpio -idmv

This will dump all of the files included in the package into the current working directory. We only really care about a couple of those files and we can find them by running

find . | grep libgconf-2.so.4

which will output the matching libraries.

./usr/lib64/libgconf-2.so.4.1.5
./usr/lib64/libgconf-2.so.4

The libgconf-2.so.4 file is the one that ldd was looking for, but it’s actually a symlink to libgconf-2.so.4.1.5 so we need both. Let’s copy these to our /opt/google/chrome/lib/ directory and try ldd again.

# Make the directory because it didn't exist already.
sudo mkdir /opt/google/chrome/lib/

# Copy the files over.
sudo cp ./usr/lib64/libgconf-2.so.4 /opt/google/chrome/lib/
sudo cp ./usr/lib64/libgconf-2.so.4.1.5 /opt/google/chrome/lib/

# Add this directory to LD_LIBRARY_PATH and run `ldd`.
export LD_LIBRARY_PATH=/opt/google/chrome/lib/:$LD_LIBRARY_PATH
ldd /opt/google/chrome/chrome | grep "not found"

Note that we’re explicitly adding the directory to LD_LIBRARY_PATH so that ldd knows about it. This happens automatically when we run google-chrome-stable, but we need to make sure we add it ourselves if we want to use these libraries outside of that context. Anyway, ldd will now output a revised list of missing depdendencies.

	libXss.so.1 => not found
	libatk-1.0.so.0 => not found
	libgtk-3.so.0 => not found
	libgdk-3.so.0 => not found
	libgdk_pixbuf-2.0.so.0 => not found
	libdbus-glib-1.so.2 => not found

Notice anything different? That’s right, no more missing libgconf-2.so.4 dependency for us. We evidently traded it for a brand new libdbus-glib-1.so.2 dependency instead.

It essentially becomes a game of whack-a-mole at this point. You fix one dependency and a new secondary dependency pops up. As you might have guessed, it’s not much fun to do this by hand. Definitely more of a script type of job. Hmmm… somebody should really make one of those.

The basic gist of what the installation script does is to run ldd, loop through the missing dependencies, find the CentOS 7 packages that provide them, download and extract them, copy over the library files, and then repeat until everything is right in the world. It’s probably easiest to just see the code.

# Loop through and install missing dependencies.
while true
do
    finished=true
    # Loop through each of the missing libraries for this round.
    while read -r line
    do
        if [[ $line == *"/"* ]]; then
            # Extract the filename when a path is present (e.g. /lib64/).
            file=`echo $line | sed 's>.*/\([^/:]*\):.*>\1>'`
        else
            # Extract the filename for missing libraries without a path.
            file=`echo $line | awk '{print $1;}'`
        fi
        # We'll require an empty round before completing.
        finished=false

        echo "Finding dependency for ${file}"

        # Find the URL for the Centos 7 RPM containing this library.
        url=$(repoquery --repofrompath=centos,http://mirror.centos.org/centos/7/os/`arch` \
            --repoid=centos -q --qf="%{location}" --whatprovides $file | \
            sed s/x86_64.rpm$/`arch`.rpm/ | \
            sed s/i686.rpm$/`arch`.rpm/g
        )

        # Download the RPM.
        wget "${url}" -O ${file}.rpm

        # Extract it and remove it.
        rpm2cpio ${file}.rpm | cpio -idmv
        rm ${file}.rpm

        # Copy it over to our library directory and clean up.
        find . | grep /${file} | xargs -n1 -I{} sudo cp {} /opt/google/chrome/lib/
        rm -rf *
    done < <(ldd /opt/google/chrome/chrome 2>&1 | grep -e "no version information" -e "not found")

    # Break once no new files have been copied in a loop.
    if [ "$finished" = true ]; then
        break
    fi
done

If you copy and paste that big hunk of code into your terminal, it should download and install all of the dependencies that you need. Once the dust settles, there will be about 30 library files that have been added to /opt/google/chrome/lib/. If my calculations are correct… everything should just work at this point.

To verify this, we can run Google Chrome in headless mode and take a screenshot

google-chrome-stable --headless --disable-gpu --screenshot \
    https://intoli.com/blog/installing-google-chrome-on-centos/

which will produce a beautiful screenshot.png file.

Screenshot

Looks good to me!

Conclusion

We’ve walked through the process of tracking down the dependencies of the Google Chrome RPM package in order to make it work on CentOS, RHEL, and Amazon Linux 6.X versions. If you were already using one of these operating systems and were trying to get Google Chrome installed then hopefully you’ve found this useful. Even if not, perhaps it was interesting just to see the process of using the powerful yum-utils to track down the additional dependencies. I wish you the best of luck with your web scraping and test automation endeavours either way!

As always, please feel free to get in touch with us here at Intoli. Our team has broad expertise in web scraping and data intelligence, and we would really love to hear about what you’re working on. We also have a pretty awesome monthly newsletter where we send out our favorite articles every month!

Suggested Articles

If you enjoyed this article, then you might also enjoy these related ones.

Breaking Out of the Chrome/WebExtension Sandbox

By Evan Sangaline
on September 14, 2018

A short guide to breaking out of the WebExtension content script sandbox.

Read more

Building a YouTube MP3 Downloader with Exodus, FFmpeg, and AWS Lambda

By Evan Sangaline
on May 21, 2018

A short guide to building a practical YouTube MP3 downloader bookmarklet using Amazon Lambda.

Read more

Running FFmpeg on AWS Lambda for 1.9% the cost of AWS Elastic Transcoder

By Evan Sangaline
on May 2, 2018

A guide to building a transcoder using Exodus, FFmpeg, and AWS Lambda.

Read more

Comments