How To Enable Hardware Acceleration on Chrome, Chromium & Puppeteer on AWS in Headless mode

How We Enhanced Remotion Performance through GPU-Accelerated Server-Side Rendering

How To Enable Hardware Acceleration on Chrome, Chromium & Puppeteer on AWS in Headless mode
Play this article

Running Google Chrome with hardware acceleration in headless mode can be more challenging than it appears. We embarked on this journey with Remotion, which is an excellent framework that enables developers to "Make Videos Programmatically". On our way, explore various Nvidia driver versions, including those from Nvidia's website and Ubuntu's official repositories.

We collaborated with the vibrant Remotion Open-source community to find the answer for using GPU with Remotion for server-side rendering. After encountering some setbacks on our way, we were able to make it work for Remotion eventually. We consolidated our findings in the Remotion Docs at Using the GPU in the Cloud and simplified the instructions for Remotion developers to make the most out of it. Although the whole research was done for Remotion, the same process will work for any headless application that requires GPU power, especially in headless mode.

Hardware Acceleration in Browser Rendering

Hardware Acceleration is to your browser what "Nitrogen Fuel" is for a gasoline car. It uses the power of specialized hardware like GPUs to make your web pages load faster and videos play smoothly. Hardware acceleration improves the part where the web page is displayed and shown to you, known as rendering. This results in a smoother and snappier browsing experience.

DLAMI from Marketplace?

One may wonder why we didn't opt for the AWS Deep Learning AMI or DLAMI. But we needed fine-grained control over the GPU drivers and utilities, and the outdated DLAMIs despite all the bloatware was not the answer. Such tasks are often challenging to achieve with pre-packaged AMIs as discussed in "Why Deep Learning AMI is Holding You Back". Tailoring the environment to our exact requirements also helped us troubleshoot issues effectively.

We experimented with multiple packages, to identify the key ones needed for Google Chrome to recognize the GPU. Despite these efforts, GPU recognition remained elusive, and in some instances, GPU drivers were crashing even at initialization. The long and arduous journey has brought us to the end of the tunnel, yet the darkness persists, and the solution remains as elusive as ever. As we decided to call it a day, We stumbled upon this thread on Puppeteer's Github issues, which did not give us any hope, but did give us a new perspective and drive to achieve our goal.

Developers are facing similar issues with hardware acceleration in headless mode

The Fresh Start

It was time to return to the drawing board and start afresh. We started with a clean base Ubuntu AMI on AWS EC2 Instance. After that, we downloaded and installed the Nvidia Tesla T4G drivers from Nvidia's official website.

Once confirmed that the GPU was working properly using nvidia-smi, We installed Google Chrome and tested it for hardware acceleration, to our relief Google Chrome finally gave a nod to work with Hardware Acceleration.

Launching an EC2 Instance

Launching an EC2 instance can be achieved in various ways, with the AWS Web Console and AWS CLI being two notable options. Regardless of the method, the important parameters are instance type and AMI selection. At the time of writing, the AMI ID for our chosen instance g4dn.xlarge is ami-053b0d53c279acc90. For launching the EC2 instance via aws-cli, the following command can be used, after replacing the placeholders with actual values.

aws ec2 run-instances \
  --instance-initiated-shutdown-behavior "terminate" \
  --image-id "ami-053b0d53c279acc90" \
  --instance-type "g4dn.xlarge" \
  --key-name "<KEY_NAME>" \
  --subnet-id "<SUBNET_ID>" \
  --security-group-ids "<SECURITY_GROUP_ID>" \
  --iam-instance-profile Arn="<ARN>" \
  --block-device-mappings '[
  {
    "DeviceName": "/dev/sda1",
    "Ebs": {
      "VolumeSize": 8,
      "VolumeType": "gp3"
    }
  }
]'

Ubuntu Upgrade

Ubuntu 22.04 AMI ami-053b0d53c279acc90 on AWS comes with Linux Kernel v5 (specifically 5.19.0-1025-aws). At this point, we have two options either we stick with Kernel v5 or Upgrade to v6 (v6.2.0-1013-aws), we decided to upgrade the Kernel. This decision is crucial as the Nvidia driver compiled for one version will not work for the other.

After launching the instance, SSH into the instance and initiate the Ubuntu packages upgrade process. To update all the installed packages including Linux Kernel execute the following one-liner.

sudo bash -c "apt update && export DEBIAN_FRONTEND=noninteractive && export NEEDRESTART_MODE=a && apt upgrade -y && reboot"

We can also split apt update, apt upgrade -y and reboot. But the reason behind doing it like this is to install these updates in non-interactive mode.

After the execution, the system will reboot with Linux Kernel Version 6. We can confirm the kernel update with uname -r. Rebooting is essential when upgrading the Ubuntu system because it loads the new Kernel on the next boot, and Nvidia drivers will subsequently be built for the updated version.

Nvidia GPU Driver Installation

The process of installing GPU drivers is straightforward when we have the correct driver for the GPU and the corresponding compatible version. Before starting the installation we need to install a couple of packages: build-essential and libvulkan1 . The former is a bundle with a variety of compilation tools required to compile the Nvidia driver. The latter, while not mandatory, is required by Google Chrome. Therefore it's a good idea to install it beforehand to enable support for Vulkan ICD loader for Nvidia, should we require it in the future.

sudo apt install -y build-essential libvulkan1

After build tools are installed, we can proceed to download and install the GPU drivers using the following commands:

DRIVER_URL="https://us.download.nvidia.com/tesla/535.104.12/NVIDIA-Linux-x86_64-535.104.12.run"
DRIVER_NAME="NVIDIA-Linux-driver.run"
wget -O "$DRIVER_NAME" "$DRIVER_URL"
sudo sh "$DRIVER_NAME" --disable-nouveau --silent
rm "$DRIVER_NAME"

Now we can run nvidia-smi to confirm the GPU drivers installation. The output will look similar to this:

nvidia-smi output

Configuring the Startup Service

We learned this the hard way: running nvidia-smi once is needed for the proper initialization of EGL and ANGLE. Google Chrome and Chromium fail to initialize EGL without this preliminary setup. To automate this process, we will create a service that runs nvidia-smi at boot time using following commands:

echo '[Unit]
Description=Run nvidia-smi at system startup

[Service]
ExecStart=/usr/bin/nvidia-smi
Type=oneshot
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target' | sudo tee /etc/systemd/system/nvidia-smi.service
sudo systemctl enable nvidia-smi.service
sudo systemctl start nvidia-smi.service

Testing the Installation

To test hardware acceleration with Google Chrome or Chromium, we can follow these steps. It's worth noting that Google Chrome has more dependencies, making it an easier choice if we plan to use the Node.js library, Puppeteer.

On the other hand, Chromium is a lightweight option with a smaller footprint of dependencies, but we can still use Puppeteer with Chromium by installing a few extra dependencies.

Chromium

  • Installation:

    Installing Chromium is straightforward, as it is readily available with Ubuntu's official software repositories. We can install it using apt with the following command.

sudo apt install -y chromium-browser
  • Testing:

    Chromium is fully prepared to handle Hardware Acceleration tasks. We can verify its readiness by launching Chromium with the specified parameters.

chromium-browser --headless --use-gl=angle \
   --use-angle=gl-egl --use-cmd-decoder=passthrough \
   --print-to-pdf=output.pdf 'chrome://gpu'

To use Puppeteer with Chromium instead of Google Chrome, there are a few extra dependencies that should be installed.

sudo apt install ca-certificates fonts-liberation \
    libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 \
    libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 \
    libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 \
    libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
    libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 \
    libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
    libxtst6 lsb-release wget xdg-utils

Google Chrome

  • Installation:

    Google Chrome can be installed from multiple options, but we will use the Google Chrome official repository for Debian. We need to add it to Ubuntu's repo list along with the public signing key.

curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/googlechrom-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/googlechrom-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt update
sudo apt install -y google-chrome-stable
  • Testing:

    Google Chrome is ready for our Hardware Acceleration tasks. We can test it by launching Google Chrome with the following parameters.

google-chrome-stable --headless --use-gl=angle \
    --use-angle=gl-egl --use-cmd-decoder=passthrough \
    --print-to-pdf=output.pdf 'chrome://gpu'

The above commands will generate output.pdf, and transfer it to your local machine to check the status of Hardware Acceleration in Google Chrome or Chromium. If the process went smoothly, the resultant PDF will look like this.

Chrome GPU status screenshot.

Working with Puppeteer

To verify GPU acceleration using the Node.js library Puppeteer, follow these steps:

  • Install Node.js, as this clean Ubuntu AMI does not include any unnecessary packages. Use the following commands:
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=18
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt update
sudo apt install nodejs
  • After installing Node.js, we can install Puppeteer by running npm i puppeteer in our code directory. Here's a sample index.js file for checking hardware acceleration:
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch({
    headless: true, 
    args: ['--use-gl=angle', '--use-angle=gl-egl'], 
  });
  const page = await browser.newPage();
  await page.goto('chrome://gpu');
  await page.waitForTimeout(2000);
  await page.screenshot({ path: 'output.png' });
  await browser.close();
})();
  • Run the script using node index.js. This will generate an output.png file containing hardware acceleration information.

Note from the Creator

Mirza has been immensely helpful in helping the “Remotion” community adopt GPU-accelerated rendering.
He's researched and written about how to obtain, configure and run EC2 containers and headless Chrome correctly in order to take advantage of graphics acceleration.
This area has been especially hard to crack, and without Mirza we would not have been able to unlock huge speed boosts.

We are super grateful for that!

--
Jonny Burger
Creator / Remotion.dev

Conclusion

With hardware acceleration now readily available in headless mode, we can harness the power of GPUs for faster and more complex rendering tasks. Our next steps involve creating a custom AMI from this instance and streamlining the process using AWS Image Builder Pipelines for efficiency. Additionally, we plan to extend hardware capabilities to Docker containers, further extending our options.

Resources