Solving OpenGL/EGL Problems When Running ORB-SLAM3 in Docker Container on NVIDIA GPU Host

12 minute read

Published:

Running ORB-SLAM3 (or other OpenGL-based computer vision applications) in Docker containers can be challenging, especially when dealing with GPU acceleration and EGL context initialization. This post documents the problems I encountered and the solutions I found when running ORB-SLAM3 in Docker on a host with an NVIDIA GPU.

Problem

ORB-SLAM3 requires OpenGL/EGL for visualization and rendering. When containerized, I encountered these errors:

MESA: error: ZINK: failed to choose pdev
libEGL warning: egl: failed to create dri2 screen
MESA: error: Failed to attach to x11 shm

This error is misleading for beginners — and even for GPT. It often sends people searching for fixes involving shm, dri, or pdev, even though those aren’t actually the cause.

Root Causes and Solutions

I identified two main causes for my problem: incorrect NVIDIA_DRIVER_CAPABILITIES configuration and the choice of base Docker image.

1. NVIDIA_DRIVER_CAPABILITIES Not Set Correctly

After setting this correctly, GLX problems were resolved (GLX works with NVIDIA GPU acceleration). However, EGL still didn’t work, which also affected ORB-SLAM3’s Map Viewer since it depends on EGL (not GLX).

Docker Compose Configuration

(Recommended)

    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu, compute, utility, video, display] 
              # capabilities: [gpu, compute, utility] is not enough!!!

Alternative:

    environment:
      - NVIDIA_DRIVER_CAPABILITIES=all
      - NVIDIA_VISIBLE_DEVICES=all
    runtime: nvidia

These two methods for injecting NVIDIA drivers are equivalent.

Docker Run Command

docker run --rm -it --gpus all -e DISPLAY -e NVIDIA_DRIVER_CAPABILITIES=all -v /tmp/.X11-unix:/tmp/.X11-unix nvidia/opengl:1.0-glvnd-devel bash

2. Not Using nvidia/opengl:1.0-glvnd-devel Base Image

I don’t fully understand why using this base image works for EGL. I noticed that it includes:

COPY 10_nvidia.json /usr/share/glvnd/egl_vendor.d/10_nvidia.json # buildkit

as seen in the Docker Hub image layers.

Perhaps someone could extract this JSON file to work with non-nvidia/opengl images like Ubuntu 22.04. For now, I simply use nvidia/opengl:1.0-glvnd-devel.

How to Verify Your Container Will Work: Check Vendor String Shows NVIDIA

You should not see mesa or llvmpipe in vendor string

EGL (Used by ORB-SLAM3 Map Viewer)

EGL is used by Wayland:

eglinfo -B
EGL vendor string: NVIDIA

GLX (for X11)

glxinfo -B
OpenGL vendor string: NVIDIA Corporation

Note: Gazebo Classic can only use GLX. Ignition Gazebo uses GLX in traditional X11 mode while EGL in Wayland mode.

Detailed Records

0. Prerequisites

  • Host system with NVIDIA GPU
  • NVIDIA drivers installed on host
  • Docker with NVIDIA Container Toolkit (nvidia-container-toolkit)
  • ORB-SLAM3 source code or pre-built binaries

Install NVIDIA Container Toolkit

# Add NVIDIA package repositories
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

# Install nvidia-container-toolkit
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

# Restart Docker daemon
sudo systemctl restart docker

1. Start a Container

xhost +local:
docker run --rm -it --gpus all -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix ubuntu:22.04 bash

Check if the NVIDIA driver is mounted:

# in container
root@cf20e34c36e2:/# nvidia-smi
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.76                 Driver Version: 550.76         CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce GTX 1080 Ti     Off |   00000000:06:00.0  On |                  N/A |
| 29%   49C    P8             15W /  250W |     303MiB /  11264MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

Good! The NVIDIA driver is working in the container.

2. Test OpenGL Programs

Let’s see if OpenGL programs run normally. We’ll use glxgears and glmark2. First, install the required packages:

# in container
# if you need mirror
sed -i 's|archive.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list \
    && sed -i 's|security.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list
apt update

apt install -y mesa-utils glmark2

Run the tests:

glxgears

root@cf20e34c36e2:/x/temp# glmark2
** GLX does not support GLX_EXT_swap_control or GLX_MESA_swap_control!
** Failed to set swap interval. Results may be bounded above by refresh rate.
=======================================================
    glmark2 2021.02
=======================================================
    OpenGL Information
    GL_VENDOR:     Mesa
    GL_RENDERER:   llvmpipe (LLVM 15.0.7, 256 bits)
    GL_VERSION:    4.5 (Compatibility Profile) Mesa 23.2.1-1ubuntu3.1~22.04.3
=======================================================
** GLX does not support GLX_EXT_swap_control or GLX_MESA_swap_control!
** Failed to set swap interval. Results may be bounded above by refresh rate.
[build] use-vbo=false: FPS: 560 FrameTime: 1.786 ms

Wait—they’re running on llvmpipe, which means there’s no hardware acceleration/GPU!

3. Check OpenGL State

# glxinfo -B
OpenGL renderer string: llvmpipe (LLVM 15.0.7, 256 bits)

# eglinfo -B
X11 platform:
EGL vendor string: Mesa Project

If glxinfo and eglinfo don’t show NVIDIA, ORB-SLAM3 cannot display its map using OpenGL/EGL.

4. OpenGL Problem in ORB-SLAM3

Run an ORB-SLAM3 sample (after installing all dependencies and building):

./Examples/Monocular-Inertial/mono_inertial_tum_vi \
    Vocabulary/ORBvoc.txt \
    Examples/Monocular-Inertial/TUM-VI.yaml \
    /dataset/tum/tumvi/exported/euroc/512_16/dataset-room1_512_16/mav0/cam0/data \
    Examples/Monocular-Inertial/TUM_TimeStamps/dataset-room1_512.txt \
    Examples/Monocular-Inertial/TUM_IMU/dataset-room1_512.txt

The ORB feature point images display, but the MapViewer is black and continuously prints:

MESA: error: Failed to attach to x11 shm

5. Check NVIDIA .so Files

# ls -l /usr/lib/x86_64-linux-gnu/*nvidia* | awk '{print $9, $10, $11}'
/usr/lib/x86_64-linux-gnu/libEGL_nvidia.so.0 -> libEGL_nvidia.so.550.76
/usr/lib/x86_64-linux-gnu/libEGL_nvidia.so.550.76  
/usr/lib/x86_64-linux-gnu/libGLESv1_CM_nvidia.so.1 -> libGLESv1_CM_nvidia.so.550.76
/usr/lib/x86_64-linux-gnu/libGLESv1_CM_nvidia.so.550.76  
/usr/lib/x86_64-linux-gnu/libGLESv2_nvidia.so.2 -> libGLESv2_nvidia.so.550.76
/usr/lib/x86_64-linux-gnu/libGLESv2_nvidia.so.550.76  
/usr/lib/x86_64-linux-gnu/libGLX_nvidia.so.0 -> libGLX_nvidia.so.550.76
/usr/lib/x86_64-linux-gnu/libGLX_nvidia.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-cfg.so.1 -> libnvidia-cfg.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-cfg.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-eglcore.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-fbc.so.1 -> libnvidia-fbc.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-fbc.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-glsi.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1 -> libnvidia-ml.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-ml.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-rtcore.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-tls.so.550.76

These files are injected by setting --gpus all, with out NVIDIA_DRIVER_CAPABILITIES=all, some critical nvidia .so files are still missing

Try 1: Use nvidia/opengl Base Image

docker run --rm -it --gpus all -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix nvidia/opengl:1.0-glvnd-devel bash

Result: glxgears cannot render, glmark2 crashes.

Try 2 (Final Solution): Add NVIDIA_DRIVER_CAPABILITIES

docker run --rm -it --gpus all -e DISPLAY -e NVIDIA_DRIVER_CAPABILITIES=all -v /tmp/.X11-unix:/tmp/.X11-unix nvidia/opengl:1.0-glvnd-devel bash
glmark2
=======================================================
    glmark2 2021.02
=======================================================
    OpenGL Information
    GL_VENDOR:     NVIDIA Corporation
    GL_RENDERER:   NVIDIA GeForce GTX 1080 Ti/PCIe/SSE2
    GL_VERSION:    4.6.0 NVIDIA 550.76
=======================================================
[build] use-vbo=false: FPS: 9481 FrameTime: 0.105 ms
[build] use-vbo=true: FPS: 20894 FrameTime: 0.048 ms
=======================================================
                                  glmark2 Score: 15187 

Good! FPS is much higher (compared to 560 with llvmpipe).

glxinfo -B
name of display: :1
display: :1  screen: 0
direct rendering: Yes
Memory info (GL_NVX_gpu_memory_info):
    Dedicated video memory: 11264 MB
    Total available memory: 11264 MB
    Currently available dedicated video memory: 10808 MB
OpenGL vendor string: NVIDIA Corporation
eglinfo -B
EGL vendor string: NVIDIA

Check the NVIDIA .so Files

# ls -l /usr/lib/x86_64-linux-gnu/*nvidia* | awk '{print $9, $10, $11}'
/usr/lib/x86_64-linux-gnu/libEGL_nvidia.so.0 -> libEGL_nvidia.so.550.76
/usr/lib/x86_64-linux-gnu/libEGL_nvidia.so.550.76  
/usr/lib/x86_64-linux-gnu/libGLESv1_CM_nvidia.so.1 -> libGLESv1_CM_nvidia.so.550.76
/usr/lib/x86_64-linux-gnu/libGLESv1_CM_nvidia.so.550.76  
/usr/lib/x86_64-linux-gnu/libGLESv2_nvidia.so.2 -> libGLESv2_nvidia.so.550.76
/usr/lib/x86_64-linux-gnu/libGLESv2_nvidia.so.550.76  
/usr/lib/x86_64-linux-gnu/libGLX_nvidia.so.0 -> libGLX_nvidia.so.550.76
/usr/lib/x86_64-linux-gnu/libGLX_nvidia.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-allocator.so.1 -> libnvidia-allocator.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-allocator.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-cfg.so.1 -> libnvidia-cfg.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-cfg.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-eglcore.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-encode.so.1 -> libnvidia-encode.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-encode.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-fbc.so.1 -> libnvidia-fbc.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-fbc.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-glsi.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-gpucomp.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1 -> libnvidia-ml.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-ml.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-ngx.so.1 -> libnvidia-ngx.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-ngx.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-nvvm.so.4 -> libnvidia-nvvm.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-nvvm.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.1 -> libnvidia-opencl.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-opticalflow.so -> libnvidia-opticalflow.so.1
/usr/lib/x86_64-linux-gnu/libnvidia-opticalflow.so.1 -> libnvidia-opticalflow.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-opticalflow.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-pkcs11-openssl3.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-pkcs11.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-ptxjitcompiler.so.1 -> libnvidia-ptxjitcompiler.so.550.76
/usr/lib/x86_64-linux-gnu/libnvidia-ptxjitcompiler.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-rtcore.so.550.76  
/usr/lib/x86_64-linux-gnu/libnvidia-tls.so.550.76  
/usr/lib/x86_64-linux-gnu/libvdpau_nvidia.so.1 -> libvdpau_nvidia.so.550.76
/usr/lib/x86_64-linux-gnu/libvdpau_nvidia.so.550.76 

So, NVIDIA_DRIVER_CAPABILITIES has a critical effect on which .so files are mounted. Important files like libnvidia-gpucomp.so.550.76 are not mounted without NVIDIA_DRIVER_CAPABILITIES.

Test: What If Using ubuntu:22.04 + NVIDIA_DRIVER_CAPABILITIES

docker run --rm -it --gpus all -e DISPLAY -e NVIDIA_DRIVER_CAPABILITIES=all -v /tmp/.X11-unix:/tmp/.X11-unix ubuntu:22.04 bash

Result: glxinfo and glmark2 work correctly (with hardware rendering), but eglinfo still shows Mesa (not NVIDIA).

This results in ORB-SLAM3 Map Viewer problems.

Summary

Running ORB-SLAM3 in Docker with NVIDIA GPU requires:

  1. Proper NVIDIA Container Toolkit setup
  2. Correct environment variables for GPU and graphics capabilities (NVIDIA_DRIVER_CAPABILITIES=all or capabilities: [gpu, compute, utility, video, display])
  3. Using the nvidia/opengl:1.0-glvnd-devel base image for EGL support
  4. Verifying vendor strings show NVIDIA (not Mesa) for both GLX and EGL

Following these solutions should resolve most OpenGL/EGL initialization problems when running ORB-SLAM3 in Docker containers on NVIDIA GPU hosts.

References

Additional Notes

Note 1: /dev/dri is Only for Integrated Graphics Cards, Not for NVIDIA

# Do not use this with NVIDIA GPU when running Docker
--device=/dev/dri:/dev/dri \

Note 2: Unnecessary Manual Injection of NVIDIA Drivers

The NVIDIA Container Toolkit automatically handles mounting the necessary NVIDIA driver libraries. Manual volume mounting of individual .so files is not required and can be problematic. The commented-out example below shows what you might be tempted to do, but it’s unnecessary:

      # for f in /usr/lib/x86_64-linux-gnu/*nvidia*.so*; do
      #   echo "- $f:$f:ro"
      # done      
      # - /usr/lib/x86_64-linux-gnu/libEGL_nvidia.so.0:/usr/lib/x86_64-linux-gnu/libEGL_nvidia.so.0:ro
      # ... (many more lines)

Note 3: OpenGL, GLVND, GLX/EGL Architecture

ComponentRole
OpenGLThe actual rendering API (glDraw*, etc.).
GLXX11-specific binding that creates OpenGL contexts and windows.
EGLCross-platform context/surface creation API for OpenGL, OpenGL ES, and Vulkan.
GLVNDVendor-neutral dispatcher for OpenGL, GLX, and EGL.
                ┌───────────────────┐
                │       Your App    │
                └──────────┬────────┘
                           │
           OpenGL API Calls│
───────────────────────────▼────────────────────────
                ┌───────────────────┐
                │       GLVND       │ (libGL, libEGL, libGLX)
                └──────────┬────────┘
                           │dispatch
───────────────────────────▼────────────────────────
     ┌──────────────────────────┬─────────────────────────┐
     │                          │                         │
┌────▼────┐               ┌─────▼─────┐             ┌────▼─────┐
│ GLX ICD │               │ EGL ICD   │             │ GL ICD    │
│ (Nvidia │               │ (Nvidia   │             │ (Nvidia   │
│ /Mesa)  │               │ /Mesa)    │             │ /Mesa)    │
└────┬────┘               └─────┬─────┘             └────┬─────┘
     │                           │                         │
─────────────── Different mechanisms to create OpenGL context ───────────────
     │                           │                         │
┌────▼────┐               ┌──────▼──────┐           ┌──────▼──────┐
│ X11     │               │ Wayland     │           │ FBDev/DRM    │
│ Window  │               │ Surface     │           │ (Headless)   │
└─────────┘               └─────────────┘           └──────────────┘

Note 4: Autoware ADE Development Environment may help

Some engineers like ADE to manage the container. But I havn’t tried yet.

https://gitlab.com/ApexAI/ade-cli

Files for docker compose

Dockerfile

# docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) --build-arg USERNAME=cw -t ubuntu-nonroot .

# Install common packages
# Replace Ubuntu mirrors with Tsinghua

# FROM ubuntu:25.04
# RUN sed -i 's|archive.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list.d/ubuntu.sources \
#     && sed -i 's|security.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list.d/ubuntu.sources \
#     && apt update


    
# ubuntu:jammy (22.04): /etc/apt/sources.list

# FROM ubuntu:22.04
FROM nvidia/opengl:1.0-glvnd-devel
RUN sed -i 's|archive.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list \
    && sed -i 's|security.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list \
    && apt update

# passwd is for delluser and delgroup
RUN apt install -y sudo adduser

# Create a non-root user
ARG USERNAME=xf
ARG UID=1000
ARG GID=1000

# --remove-home ubuntu requires perl, so skip
RUN deluser ubuntu || true \
    && delgroup ubuntu || true \
    && groupadd -g 1000 $USERNAME \
    && useradd -m -u 1000 -g $USERNAME -s /bin/bash $USERNAME \
    && echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# RUN groupadd -g $GID $USERNAME \
#     && useradd -m -u $UID -g $USERNAME -s /bin/bash $USERNAME \
#     && echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# Switch to the new user
USER $USERNAME
WORKDIR /home/$USERNAME
ENV HOME=/home/$USERNAME

# need relogin and apt update to populate the database
RUN sudo apt install -y command-not-found
RUN sudo apt update

# to avoid tzdata require input in the following apt installation
ENV TZ=Asia/Shanghai
RUN sudo ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ | sudo tee /etc/timezone

RUN sudo apt install -y nano vim build-essential cmake git

# ------------------------------------------------------------------- python
RUN sudo apt install -y python3 python3-pip python3-virtualenv python3-dev

# RUN pip config set global.index-url https://mirrors.cloud.tencent.com/pypi/simple
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

# pip install some binaries to .local/bin folder
ENV PATH=$HOME/pyenvs/ai/bin:$HOME/.local/bin:$PATH

ENV AIPY=$HOME/pyenvs/ai
RUN virtualenv $AIPY
RUN echo "source $HOME/pyenvs/ai/bin/activate" >> $HOME/.bashrc

RUN $AIPY/bin/python -m pip install --upgrade pip
RUN $AIPY/bin/python -m pip install setuptools wheel

# RUN sudo apt update
RUN sudo apt install -y libopencv-dev libopencv-contrib-dev libeigen3-dev libboost-all-dev

# to run GUI applications
RUN sudo apt install -y \
    libx11-dev libxext6 libxrender1 libxtst6 libxi6 \
    libgl1 libglx-mesa0 mesa-utils \
    libgl1-mesa-dri libglu1-mesa \
    libglfw3-dev libglew-dev \
    libxrandr-dev libxinerama-dev libxcursor-dev \
    x11-xserver-utils x11-utils xauth

# run glxgears to check is opengl is ok inside docker

# Keep container alive for background dev
CMD ["sleep", "infinity"]


docker-compose.yml

services:

  # =========================================================
  # Release profile: use prebuilt stable image
  # =========================================================
  cvrelease:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        UID: ${UID:-1000}
        GID: ${GID:-1000}
        USERNAME: xf  
    hostname: docker-cv
    profiles: ["release"]
    restart: unless-stopped
    extra_hosts:
      - "docker-cv:127.0.0.1"
    volumes:
      - /x:/x
      - /data:/data
      - /tmp/.X11-unix:/tmp/.X11-unix:rw

    environment:
      - DISPLAY=${DISPLAY:-:0}

    working_dir: /x
    tty: true
    network_mode: host
    shm_size: "2g"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu, compute, utility, video, display] 

start command

xhost +local:
sudo -E docker compose --profile release up -d

The xhost +local: could be ran after the container startup